mirror of
https://github.com/splunk/security_content
synced 2026-06-08 17:32:49 +00:00
669 lines
29 KiB
Python
669 lines
29 KiB
Python
"""
|
|
Published in response to CVE-2021-44228, this playbook accepts a list of hosts and filenames to remediate on the endpoint. If filenames are provided, the endpoints will be searched and then the user can approve deletion. Then the user is prompted to quarantine the endpoint.
|
|
"""
|
|
|
|
|
|
import phantom.rules as phantom
|
|
import json
|
|
from datetime import datetime, timedelta
|
|
|
|
|
|
def on_start(container):
|
|
phantom.debug('on_start() called')
|
|
|
|
# call 'filepath_decision' block
|
|
filepath_decision(container=container)
|
|
|
|
return
|
|
|
|
def locate_files(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
|
|
phantom.debug("locate_files() called")
|
|
|
|
# phantom.debug('Action: {0} {1}'.format(action['name'], ('SUCCEEDED' if success else 'FAILED')))
|
|
|
|
################################################################################
|
|
# Run a search to locate files. Contains custom code.
|
|
################################################################################
|
|
|
|
playbook_input_filepath = phantom.collect2(container=container, datapath=["playbook_input:filepath"])
|
|
playbook_input_ip_or_hostname = phantom.collect2(container=container, datapath=["playbook_input:ip_or_hostname"])
|
|
|
|
parameters = []
|
|
|
|
# build parameters list for 'locate_files' call
|
|
for playbook_input_filepath_item in playbook_input_filepath:
|
|
for playbook_input_ip_or_hostname_item in playbook_input_ip_or_hostname:
|
|
if playbook_input_ip_or_hostname_item[0] is not None:
|
|
parameters.append({
|
|
"command": playbook_input_filepath_item[0],
|
|
"ip_hostname": playbook_input_ip_or_hostname_item[0],
|
|
})
|
|
|
|
################################################################################
|
|
## Custom Code Start
|
|
################################################################################
|
|
|
|
parameters = []
|
|
host_search = {}
|
|
|
|
# Treat ip_or_hostname and filePath as a paired unit and iterate through them,
|
|
# then create a dictionary for each host with a list of its filepaths
|
|
for playbook_input_filepath_item, playbook_input_ip_or_hostname_item in zip(playbook_input_filepath, playbook_input_ip_or_hostname):
|
|
if playbook_input_ip_or_hostname_item[0] in host_search.keys():
|
|
host_search[playbook_input_ip_or_hostname_item[0]].append(playbook_input_filepath_item[0])
|
|
else:
|
|
host_search[playbook_input_ip_or_hostname_item[0]] = [playbook_input_filepath_item[0]]
|
|
|
|
# Iterate through the host dictionary and generation one search string that checks all filepaths per host.
|
|
# This ensures that we are only connecting to each host once.
|
|
for k,v in host_search.items():
|
|
script_str = f'''RESULT=""; for i in "{'" "'.join(v)}"; do if [ -f "$i" ]; then RESULT="${{RESULT}} true"; else RESULT="${{RESULT}} false"; fi; done; echo $RESULT'''
|
|
parameters.append({
|
|
"ip_hostname": k,
|
|
"command": script_str
|
|
})
|
|
|
|
# Save the host_search dictionary into temporary data to access downstream.
|
|
# This makes it easier to compare the results of locate files with the filepaths and hosts
|
|
phantom.save_run_data(value=json.dumps(host_search), key="host_dictionary")
|
|
|
|
################################################################################
|
|
## Custom Code End
|
|
################################################################################
|
|
|
|
phantom.act("execute program", parameters=parameters, name="locate_files", assets=["ssh"], callback=file_search_decision)
|
|
|
|
return
|
|
|
|
|
|
def file_search_decision(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
|
|
phantom.debug("file_search_decision() called")
|
|
|
|
################################################################################
|
|
# Determine if at least one file was found
|
|
################################################################################
|
|
|
|
# check for 'if' condition 1
|
|
found_match_1 = phantom.decision(
|
|
container=container,
|
|
conditions=[
|
|
["true", "in", "locate_files:action_result.data.*.output"]
|
|
])
|
|
|
|
# call connected blocks if condition 1 matched
|
|
if found_match_1:
|
|
generate_deletion_commands(action=action, success=success, container=container, results=results, handle=handle)
|
|
return
|
|
|
|
# check for 'else' condition 2
|
|
join_dedup_hostnames(action=action, success=success, container=container, results=results, handle=handle)
|
|
|
|
return
|
|
|
|
|
|
def generate_deletion_commands(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
|
|
phantom.debug("generate_deletion_commands() called")
|
|
|
|
################################################################################
|
|
# Generate a prompt along with one deletion command per host.
|
|
################################################################################
|
|
|
|
locate_files_result_data = phantom.collect2(container=container, datapath=["locate_files:action_result.parameter.ip_hostname","locate_files:action_result.data.*.output"], action_results=results)
|
|
|
|
locate_files_parameter_ip_hostname = [item[0] for item in locate_files_result_data]
|
|
locate_files_result_item_1 = [item[1] for item in locate_files_result_data]
|
|
|
|
generate_deletion_commands__prompt_content = None
|
|
generate_deletion_commands__deletion_command = None
|
|
generate_deletion_commands__host = None
|
|
|
|
################################################################################
|
|
## Custom Code Start
|
|
################################################################################
|
|
|
|
generate_deletion_commands__prompt_content = ""
|
|
generate_deletion_commands__host = []
|
|
generate_deletion_commands__deletion_command = []
|
|
|
|
# Retrieve previously saved key
|
|
host_dictionary = json.loads(phantom.get_run_data(key="host_dictionary"))
|
|
# Iterate through the paired host and locate files action result
|
|
for hostname, action_result in zip(locate_files_parameter_ip_hostname, locate_files_result_item_1):
|
|
|
|
# Ensure at least one file was found for that host
|
|
if "true" in action_result.split(' '):
|
|
|
|
# Attach this host to list of deletion hosts
|
|
generate_deletion_commands__host.append(hostname)
|
|
|
|
# Begin building deletion powershell script and prompt message
|
|
deletion_string = f'''for i in '''
|
|
generate_deletion_commands__prompt_content += f"### {hostname}\n\n"
|
|
for filepath, result in zip(host_dictionary[hostname], action_result.split(' ')):
|
|
if result == "true":
|
|
generate_deletion_commands__prompt_content += f"- {filepath}\n"
|
|
deletion_string += f'"{filepath}" '
|
|
|
|
# Remove trailing comma and attach one deletion command
|
|
deletion_string = deletion_string.rstrip(',')
|
|
deletion_string += '; do rm -v $i; done;'
|
|
generate_deletion_commands__deletion_command.append(deletion_string)
|
|
|
|
################################################################################
|
|
## Custom Code End
|
|
################################################################################
|
|
|
|
phantom.save_run_data(key="generate_deletion_commands:prompt_content", value=json.dumps(generate_deletion_commands__prompt_content))
|
|
phantom.save_run_data(key="generate_deletion_commands:deletion_command", value=json.dumps(generate_deletion_commands__deletion_command))
|
|
phantom.save_run_data(key="generate_deletion_commands:host", value=json.dumps(generate_deletion_commands__host))
|
|
|
|
deletion_confirmation(container=container)
|
|
|
|
return
|
|
|
|
|
|
def deletion_confirmation(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
|
|
phantom.debug("deletion_confirmation() called")
|
|
|
|
################################################################################
|
|
# Prompt the user to confirm deletion
|
|
################################################################################
|
|
|
|
# set user and message variables for phantom.prompt call
|
|
|
|
user = "Incident Commander"
|
|
message = """SOAR found results for the following files. Please review the returned list and confirm if they should be deleted.\n\n \n{0}"""
|
|
|
|
# parameter list for template variable replacement
|
|
parameters = [
|
|
"generate_deletion_commands:custom_function:prompt_content"
|
|
]
|
|
|
|
# responses
|
|
response_types = [
|
|
{
|
|
"prompt": "Type 'confirm' to delete",
|
|
"options": {
|
|
"type": "message",
|
|
},
|
|
}
|
|
]
|
|
|
|
phantom.prompt2(container=container, user=user, message=message, respond_in_mins=30, name="deletion_confirmation", parameters=parameters, response_types=response_types, callback=deletion_decision)
|
|
|
|
return
|
|
|
|
|
|
def deletion_decision(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
|
|
phantom.debug("deletion_decision() called")
|
|
|
|
################################################################################
|
|
# Determine if user wants to delete files
|
|
################################################################################
|
|
|
|
# check for 'if' condition 1
|
|
found_match_1 = phantom.decision(
|
|
container=container,
|
|
conditions=[
|
|
["deletion_confirmation:action_result.summary.responses.0", "==", "confirm"]
|
|
])
|
|
|
|
# call connected blocks if condition 1 matched
|
|
if found_match_1:
|
|
delete_files(action=action, success=success, container=container, results=results, handle=handle)
|
|
return
|
|
|
|
# check for 'else' condition 2
|
|
join_dedup_hostnames(action=action, success=success, container=container, results=results, handle=handle)
|
|
|
|
return
|
|
|
|
|
|
def delete_files(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
|
|
phantom.debug("delete_files() called")
|
|
|
|
# phantom.debug('Action: {0} {1}'.format(action['name'], ('SUCCEEDED' if success else 'FAILED')))
|
|
|
|
################################################################################
|
|
# Iterate through host and deletion command. Contains custom code.
|
|
################################################################################
|
|
|
|
generate_deletion_commands__deletion_command = json.loads(phantom.get_run_data(key="generate_deletion_commands:deletion_command"))
|
|
generate_deletion_commands__host = json.loads(phantom.get_run_data(key="generate_deletion_commands:host"))
|
|
|
|
parameters = []
|
|
|
|
if generate_deletion_commands__host is not None:
|
|
parameters.append({
|
|
"command": generate_deletion_commands__deletion_command,
|
|
"ip_hostname": generate_deletion_commands__host,
|
|
})
|
|
|
|
################################################################################
|
|
## Custom Code Start
|
|
################################################################################
|
|
|
|
parameters = []
|
|
for del_cmd, host in zip(generate_deletion_commands__deletion_command, generate_deletion_commands__host):
|
|
parameters.append({
|
|
"command": del_cmd,
|
|
"ip_hostname": host,
|
|
})
|
|
|
|
################################################################################
|
|
## Custom Code End
|
|
################################################################################
|
|
|
|
phantom.act("execute program", parameters=parameters, name="delete_files", assets=["ssh"], callback=join_dedup_hostnames)
|
|
|
|
return
|
|
|
|
|
|
def filepath_decision(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
|
|
phantom.debug("filepath_decision() called")
|
|
|
|
################################################################################
|
|
# Determine if filepath is present in playbook inputs
|
|
################################################################################
|
|
|
|
# check for 'if' condition 1
|
|
found_match_1 = phantom.decision(
|
|
container=container,
|
|
conditions=[
|
|
["playbook_input:filepath", "!=", ""]
|
|
])
|
|
|
|
# call connected blocks if condition 1 matched
|
|
if found_match_1:
|
|
locate_files(action=action, success=success, container=container, results=results, handle=handle)
|
|
return
|
|
|
|
# check for 'else' condition 2
|
|
join_dedup_hostnames(action=action, success=success, container=container, results=results, handle=handle)
|
|
|
|
return
|
|
|
|
|
|
def format_host_list_prompt(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
|
|
phantom.debug("format_host_list_prompt() called")
|
|
|
|
################################################################################
|
|
# Format a list of the hosts. This will feed both env var prompt and shut down
|
|
# prompts.
|
|
################################################################################
|
|
|
|
template = """%%\n- {0}\n%%"""
|
|
|
|
# parameter list for template variable replacement
|
|
parameters = [
|
|
"dedup_hostnames:custom_function_result.data.*.item"
|
|
]
|
|
|
|
################################################################################
|
|
## Custom Code Start
|
|
################################################################################
|
|
|
|
# Write your custom code here...
|
|
|
|
################################################################################
|
|
## Custom Code End
|
|
################################################################################
|
|
|
|
phantom.format(container=container, template=template, parameters=parameters, name="format_host_list_prompt")
|
|
|
|
quarantine_prompt(container=container)
|
|
|
|
return
|
|
|
|
|
|
def quarantine_prompt(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
|
|
phantom.debug("quarantine_prompt() called")
|
|
|
|
################################################################################
|
|
# Offer the user options to quarantine the affected endpoints
|
|
################################################################################
|
|
|
|
# set user and message variables for phantom.prompt call
|
|
|
|
user = "Incident Commander"
|
|
message = """Choose an action you would like to take on the following hosts and then type confirm. The same selected action will be performed on every host.\n\n \n### Available Actions:\n- Restrict Outbound Traffic\n(Sets a firewall policy to prevent all outbound traffic. This may disrupt domain authentication for all but cached credentials.)\n- Shutdown Host (This is a forced shutdown)\n\n \n### Target Hosts\n{0}"""
|
|
|
|
# parameter list for template variable replacement
|
|
parameters = [
|
|
"format_host_list_prompt:formatted_data"
|
|
]
|
|
|
|
# responses
|
|
response_types = [
|
|
{
|
|
"prompt": "Available Actions",
|
|
"options": {
|
|
"type": "list",
|
|
"choices": [
|
|
"Restrict Outbound Traffic",
|
|
"Shutdown",
|
|
"Restrict Outbound Traffic and Shutdown",
|
|
"Do Nothing"
|
|
],
|
|
},
|
|
},
|
|
{
|
|
"prompt": "Type \"confirm\"",
|
|
"options": {
|
|
"type": "message",
|
|
},
|
|
}
|
|
]
|
|
|
|
phantom.prompt2(container=container, user=user, message=message, respond_in_mins=30, name="quarantine_prompt", parameters=parameters, response_types=response_types, callback=quarantine_decision)
|
|
|
|
return
|
|
|
|
|
|
def quarantine_decision(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
|
|
phantom.debug("quarantine_decision() called")
|
|
|
|
################################################################################
|
|
# Determine which action the user selected
|
|
################################################################################
|
|
|
|
# check for 'if' condition 1
|
|
found_match_1 = phantom.decision(
|
|
container=container,
|
|
logical_operator="and",
|
|
conditions=[
|
|
["quarantine_prompt:action_result.summary.responses.0", "==", "Restrict Outbound Traffic"],
|
|
["quarantine_prompt:action_result.summary.responses.1", "==", "confirm"]
|
|
])
|
|
|
|
# call connected blocks if condition 1 matched
|
|
if found_match_1:
|
|
block_outbound_traffic(action=action, success=success, container=container, results=results, handle=handle)
|
|
return
|
|
|
|
# check for 'elif' condition 2
|
|
found_match_2 = phantom.decision(
|
|
container=container,
|
|
logical_operator="and",
|
|
conditions=[
|
|
["quarantine_prompt:action_result.summary.responses.0", "==", "Shutdown"],
|
|
["quarantine_prompt:action_result.summary.responses.1", "==", "confirm"]
|
|
])
|
|
|
|
# call connected blocks if condition 2 matched
|
|
if found_match_2:
|
|
shutdown(action=action, success=success, container=container, results=results, handle=handle)
|
|
return
|
|
|
|
# check for 'elif' condition 3
|
|
found_match_3 = phantom.decision(
|
|
container=container,
|
|
logical_operator="and",
|
|
conditions=[
|
|
["quarantine_prompt:action_result.summary.responses.0", "==", "Restrict Outbound Traffic and Shutdown"],
|
|
["quarantine_prompt:action_result.summary.responses.1", "==", "confirm"]
|
|
])
|
|
|
|
# call connected blocks if condition 3 matched
|
|
if found_match_3:
|
|
block_and_shutdown(action=action, success=success, container=container, results=results, handle=handle)
|
|
return
|
|
|
|
# check for 'else' condition 4
|
|
join_format_custom_note(action=action, success=success, container=container, results=results, handle=handle)
|
|
|
|
return
|
|
|
|
|
|
def block_outbound_traffic(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
|
|
phantom.debug("block_outbound_traffic() called")
|
|
|
|
# phantom.debug('Action: {0} {1}'.format(action['name'], ('SUCCEEDED' if success else 'FAILED')))
|
|
|
|
################################################################################
|
|
# Add firewall rule blocking outbound traffic
|
|
################################################################################
|
|
|
|
dedup_hostnames_data = phantom.collect2(container=container, datapath=["dedup_hostnames:custom_function_result.data.*.item"])
|
|
|
|
parameters = []
|
|
|
|
# build parameters list for 'block_outbound_traffic' call
|
|
for dedup_hostnames_data_item in dedup_hostnames_data:
|
|
if dedup_hostnames_data_item[0] is not None:
|
|
parameters.append({
|
|
"command": "sudo -S iptables -I OUTPUT -p all 0.0.0.0/0 -j DROP SPLUNK_SOAR_BLOCK",
|
|
"ip_hostname": dedup_hostnames_data_item[0],
|
|
})
|
|
|
|
################################################################################
|
|
## Custom Code Start
|
|
################################################################################
|
|
|
|
# Write your custom code here...
|
|
|
|
################################################################################
|
|
## Custom Code End
|
|
################################################################################
|
|
|
|
phantom.act("execute program", parameters=parameters, name="block_outbound_traffic", assets=["ssh"], callback=join_format_custom_note)
|
|
|
|
return
|
|
|
|
|
|
def shutdown(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
|
|
phantom.debug("shutdown() called")
|
|
|
|
# phantom.debug('Action: {0} {1}'.format(action['name'], ('SUCCEEDED' if success else 'FAILED')))
|
|
|
|
################################################################################
|
|
# Shutdown machine
|
|
################################################################################
|
|
|
|
dedup_hostnames_data = phantom.collect2(container=container, datapath=["dedup_hostnames:custom_function_result.data.*.item"])
|
|
|
|
parameters = []
|
|
|
|
# build parameters list for 'shutdown' call
|
|
for dedup_hostnames_data_item in dedup_hostnames_data:
|
|
if dedup_hostnames_data_item[0] is not None:
|
|
parameters.append({
|
|
"command": "shutdown now",
|
|
"ip_hostname": dedup_hostnames_data_item[0],
|
|
})
|
|
|
|
################################################################################
|
|
## Custom Code Start
|
|
################################################################################
|
|
|
|
# Write your custom code here...
|
|
|
|
################################################################################
|
|
## Custom Code End
|
|
################################################################################
|
|
|
|
phantom.act("execute program", parameters=parameters, name="shutdown", assets=["ssh"], callback=join_format_custom_note)
|
|
|
|
return
|
|
|
|
|
|
def block_and_shutdown(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
|
|
phantom.debug("block_and_shutdown() called")
|
|
|
|
# phantom.debug('Action: {0} {1}'.format(action['name'], ('SUCCEEDED' if success else 'FAILED')))
|
|
|
|
################################################################################
|
|
# Add firewall rule and then shutdown machine
|
|
################################################################################
|
|
|
|
dedup_hostnames_data = phantom.collect2(container=container, datapath=["dedup_hostnames:custom_function_result.data.*.item"])
|
|
|
|
parameters = []
|
|
|
|
# build parameters list for 'block_and_shutdown' call
|
|
for dedup_hostnames_data_item in dedup_hostnames_data:
|
|
if dedup_hostnames_data_item[0] is not None:
|
|
parameters.append({
|
|
"command": "sudo -S iptables -I OUTPUT -p all 0.0.0.0/0 -j DROP SPLUNK_SOAR_BLOCK && shutdown now",
|
|
"ip_hostname": dedup_hostnames_data_item[0],
|
|
})
|
|
|
|
################################################################################
|
|
## Custom Code Start
|
|
################################################################################
|
|
|
|
# Write your custom code here...
|
|
|
|
################################################################################
|
|
## Custom Code End
|
|
################################################################################
|
|
|
|
phantom.act("execute program", parameters=parameters, name="block_and_shutdown", assets=["ssh"], callback=join_format_custom_note)
|
|
|
|
return
|
|
|
|
|
|
def summary_note(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
|
|
phantom.debug("summary_note() called")
|
|
|
|
################################################################################
|
|
# Leave a summary note
|
|
################################################################################
|
|
|
|
format_custom_note__output = json.loads(phantom.get_run_data(key="format_custom_note:output"))
|
|
|
|
################################################################################
|
|
## Custom Code Start
|
|
################################################################################
|
|
|
|
|
|
################################################################################
|
|
## Custom Code End
|
|
################################################################################
|
|
|
|
phantom.add_note(container=container, content=format_custom_note__output, note_format="markdown", note_type="general", title="SSH Log4j Response")
|
|
|
|
return
|
|
|
|
|
|
def join_dedup_hostnames(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
|
|
phantom.debug("join_dedup_hostnames() called")
|
|
|
|
# if the joined function has already been called, do nothing
|
|
if phantom.get_run_data(key="join_dedup_hostnames_called"):
|
|
return
|
|
|
|
# save the state that the joined function has now been called
|
|
phantom.save_run_data(key="join_dedup_hostnames_called", value="dedup_hostnames")
|
|
|
|
# call connected block "dedup_hostnames"
|
|
dedup_hostnames(container=container, handle=handle)
|
|
|
|
return
|
|
|
|
|
|
def dedup_hostnames(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
|
|
phantom.debug("dedup_hostnames() called")
|
|
|
|
playbook_input_ip_or_hostname = phantom.collect2(container=container, datapath=["playbook_input:ip_or_hostname"])
|
|
|
|
playbook_input_ip_or_hostname_values = [item[0] for item in playbook_input_ip_or_hostname]
|
|
|
|
parameters = []
|
|
|
|
parameters.append({
|
|
"input_list": playbook_input_ip_or_hostname_values,
|
|
})
|
|
|
|
################################################################################
|
|
## Custom Code Start
|
|
################################################################################
|
|
|
|
# Write your custom code here...
|
|
|
|
################################################################################
|
|
## Custom Code End
|
|
################################################################################
|
|
|
|
phantom.custom_function(custom_function="community/list_deduplicate", parameters=parameters, name="dedup_hostnames", callback=format_host_list_prompt)
|
|
|
|
return
|
|
|
|
|
|
def join_format_custom_note(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
|
|
phantom.debug("join_format_custom_note() called")
|
|
|
|
# if the joined function has already been called, do nothing
|
|
if phantom.get_run_data(key="join_format_custom_note_called"):
|
|
return
|
|
|
|
# save the state that the joined function has now been called
|
|
phantom.save_run_data(key="join_format_custom_note_called", value="format_custom_note")
|
|
|
|
# call connected block "format_custom_note"
|
|
format_custom_note(container=container, handle=handle)
|
|
|
|
return
|
|
|
|
|
|
def format_custom_note(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
|
|
phantom.debug("format_custom_note() called")
|
|
|
|
################################################################################
|
|
# Format a dynamic summary note from playbook.get_summary()
|
|
################################################################################
|
|
|
|
format_custom_note__output = None
|
|
|
|
################################################################################
|
|
## Custom Code Start
|
|
################################################################################
|
|
format_custom_note__output = ""
|
|
playbook_summary = phantom.get_summary()
|
|
if 'result' in playbook_summary.keys() and playbook_summary['result']:
|
|
for result_item in playbook_summary['result']:
|
|
format_custom_note__output += f"#### Action - {result_item['name']}: {result_item['message']}\n"
|
|
# Generate app run summary for each action
|
|
if 'app_runs' in result_item.keys() and result_item['app_runs']:
|
|
for app_run_item in result_item['app_runs']:
|
|
format_custom_note__output += f"- app_run_id: {app_run_item['app_run_id']}\n"
|
|
for k,v in app_run_item['parameter'].items():
|
|
if k != 'context':
|
|
format_custom_note__output += f" - {k}: {v}\n"
|
|
format_custom_note__output += f"- summary: {app_run_item['summary']}\n"
|
|
format_custom_note__output += "\n"
|
|
|
|
################################################################################
|
|
## Custom Code End
|
|
################################################################################
|
|
|
|
phantom.save_run_data(key="format_custom_note:output", value=json.dumps(format_custom_note__output))
|
|
|
|
summary_note(container=container)
|
|
|
|
return
|
|
|
|
|
|
def on_finish(container, summary):
|
|
phantom.debug("on_finish() called")
|
|
|
|
################################################################################
|
|
## Custom Code Start
|
|
################################################################################
|
|
|
|
# This function is called after all actions are completed.
|
|
# summary of all the action and/or all details of actions
|
|
# can be collected here.
|
|
|
|
# summary_json = phantom.get_summary()
|
|
# if 'result' in summary_json:
|
|
# for action_result in summary_json['result']:
|
|
# if 'action_run_id' in action_result:
|
|
# action_results = phantom.get_action_results(action_run_id=action_result['action_run_id'], result_data=False, flatten=False)
|
|
# phantom.debug(action_results)
|
|
|
|
################################################################################
|
|
## Custom Code End
|
|
################################################################################
|
|
|
|
return |