예제 #1
0
def get_effective_user(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
    phantom.debug("get_effective_user() called")

    ################################################################################
    # Find the user and user type that launched this playbook.
    ################################################################################

    get_effective_user__user_id = None
    get_effective_user__user_type = None

    ################################################################################
    ## Custom Code Start
    ################################################################################

    effective_user_id = phantom.get_effective_user()
    url = phantom.build_phantom_rest_url('ph_user', effective_user_id)
    response_json = phantom.requests.get(url, verify=False).json()
    
    get_effective_user__user_type = response_json['type']
    get_effective_user__user_id = effective_user_id
        

    ################################################################################
    ## Custom Code End
    ################################################################################

    phantom.save_run_data(key="get_effective_user:user_id", value=json.dumps(get_effective_user__user_id))
    phantom.save_run_data(key="get_effective_user:user_type", value=json.dumps(get_effective_user__user_type))

    user_decision(container=container)

    return
예제 #2
0
def select_indicators(action=None,
                      success=None,
                      container=None,
                      results=None,
                      handle=None,
                      filtered_artifacts=None,
                      filtered_results=None,
                      custom_function=None,
                      **kwargs):
    phantom.debug("select_indicators() called")

    # set user and message variables for phantom.prompt call
    custom_format__output = json.loads(
        phantom.get_run_data(key="custom_format:output"))

    user_id = phantom.get_effective_user()
    url = phantom.build_phantom_rest_url('ph_user', user_id)
    response = phantom.requests.get(url, verify=False).json()
    user = response['username']
    message = """Please review the list of suspect indicators and select an action.\n\n{0}""".format(
        custom_format__output)

    indicator_records = phantom.collect2(
        container=container,
        datapath=[
            "get_suspect_indicators:custom_function_result.data.*.indicator_value"
        ],
        action_results=results)

    indicator_value_list = [item[0] for item in indicator_records]

    # dynamic response generation
    response_types = []
    parameters = None
    for ind_val in indicator_value_list:
        response_types.append({
            "prompt": "{0}".format(ind_val),
            "options": {
                "type": "list",
                "choices": ["Block", "Tag as Safe", "Do Nothing"]
            }
        })

    phantom.prompt2(container=container,
                    user=user,
                    message=message,
                    respond_in_mins=30,
                    name="select_indicators",
                    parameters=parameters,
                    response_types=response_types,
                    callback=process_responses)

    return
def event_details(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs):
    phantom.debug("event_details() called")

    ################################################################################
    # A dynamic prompt to list out details for each container so that the user can 
    # decide which to merge.
    ################################################################################

    # set user and message variables for phantom.prompt call
    effective_user_id = phantom.get_effective_user()
    url = phantom.build_phantom_rest_url('ph_user', effective_user_id)
    response = phantom.requests.get(url, verify=False).json()
    user = response.get('username')
    message = """{0}"""

    # parameter list for template variable replacement
    parameters = [
        "custom_format:custom_function:output",
    ]
    
    # fetch data for dynamic response
    container_data = phantom.collect2(container=container, datapath=['find_related_events:custom_function_result.data.*.container_id', 'find_related_events:custom_function_result.data.*.container_name'], action_results=results)
    container_id_list = [item[0] for item in container_data]
    container_name_list = [item[1] for item in container_data]
    
    #Dynamic Responses:
    response_types = []
    for container_id, container_name in zip(container_id_list, container_name_list):
        response_types.append({
                "prompt": "Event {0}: {1}".format(container_id, container_name),
                "options": {
                    "type": "list",
                    "choices": [
                        "Merge Into Case",
                        "Ignore",
                    ]
                },
            })
        
    phantom.save_run_data(value=json.dumps(container_id_list), key="container_list", auto=True)

    phantom.prompt2(container=container, user=user, message=message, respond_in_mins=30, name="event_details", parameters=parameters, response_types=response_types, callback=process_responses)

    return
예제 #4
0
def container_update(container_input=None,
                     name=None,
                     description=None,
                     label=None,
                     owner=None,
                     sensitivity=None,
                     severity=None,
                     status=None,
                     tags=None,
                     input_json=None,
                     **kwargs):
    """
    Allows updating various attributes of a container in a single custom function. Any attributes of a container not listed can be updated via the input_json parameter. 
    
    Args:
        container_input (CEF type: phantom container id): Supports a container id or container dictionary
        name: Optional parameter to change container name
        description: Optional parameter to change the container description
        label (CEF type: phantom container label): Optional parameter to change the container label
        owner: Optional parameter to change the container owner. Accepts a username or role name or keyword "current" to set the currently running playbook user as the owner.
        sensitivity: Optional parameter to change the container sensitivity. 
        severity: Optional parameter to change the container severity.
        status: Optional parameter to change the container status.
        tags: Optional parameter to change the container tags. Must be in the format of a comma separated list.
        input_json: Optional parameter to modify any extra attributes of a container. Input_json will be merged with other inputs. In the event of a conflict, input_json will take precedence.
    
    Returns a JSON-serializable object that implements the configured data paths:
        
    """
    ############################ Custom Code Goes Below This Line #################################
    import json
    import phantom.rules as phantom

    outputs = {}
    update_dict = {}

    if isinstance(container_input, int):
        container = phantom.get_container(container_input)
    elif isinstance(container_input, dict):
        container = container_input
    else:
        raise TypeError("container_input is neither a int or a dictionary")

    if name:
        update_dict['name'] = name
    if description:
        update_dict['description'] = description
    if label:
        update_dict['label'] = label
    if owner:
        # If keyword 'current' entered then translate effective_user id to a username
        if owner.lower() == 'current':
            update_dict['owner_id'] = phantom.get_effective_user()
        else:
            # Attempt to translate name to owner_id
            url = phantom.build_phantom_rest_url(
                'ph_user') + f'?_filter_username="******"'
            data = phantom.requests.get(url, verify=False).json().get('data')
            if data and len(data) == 1:
                update_dict['owner_id'] = data[0]['id']
            elif data and len(data) > 1:
                phantom.error(f'Multiple matches for owner "{owner}"')
            else:
                # Attempt to translate name to role_id
                url = phantom.build_phantom_rest_url(
                    'role') + f'?_filter_name="{owner}"'
                data = phantom.requests.get(url,
                                            verify=False).json().get('data')
                if data and len(data) == 1:
                    update_dict['role_id'] = data[0]['id']
                elif data and len(data) > 1:
                    phantom.error(f'Multiple matches for role "{owner}"')
                else:
                    phantom.error(f'"{owner}" is not a valid username or role')
    if sensitivity:
        update_dict['sensitivity'] = sensitivity
    if severity:
        update_dict['severity'] = severity
    if status:
        update_dict['status'] = status
    if tags:
        tags = tags.replace(" ", "").split(",")
        update_dict['tags'] = tags
    if input_json:
        json_dict = json.loads(input_json)
        # Merge dictionaries together. The second argument, "**json_dict" will take precedence and overwrite any duplicate parameters.
        update_dict = {**update_dict, **json_dict}

    if update_dict:
        phantom.debug(
            'Updating container {0} with the following information: "{1}"'.
            format(container['id'], update_dict))
        phantom.update(container, update_dict)
    else:
        phantom.debug(
            "Valid container entered but no valid container changes provided.")

    # Return a JSON-serializable object
    assert json.dumps(
        outputs
    )  # Will raise an exception if the :outputs: object is not JSON-serializable
    return outputs
예제 #5
0
def protect_prompt(action=None,
                   success=None,
                   container=None,
                   results=None,
                   handle=None,
                   filtered_artifacts=None,
                   filtered_results=None,
                   custom_function=None,
                   **kwargs):
    phantom.debug("protect_prompt() called")

    # set user and message variables for phantom.prompt call

    user_id = phantom.get_effective_user()
    url = phantom.build_phantom_rest_url('ph_user', user_id)
    response = phantom.requests.get(url, verify=False).json()
    user = response['username']
    message = """{0}"""

    # parameter list for template variable replacement
    parameters = ["format_prompt:formatted_data"]

    device_data = phantom.collect2(
        container=container,
        datapath=['run_asset_query:action_result.data.*.nt_host'],
        action_results=results)
    device_list = [item[0] for item in device_data]
    user_data = phantom.collect2(
        container=container,
        datapath=['run_identity_query:action_result.data.*.email'],
        action_results=results)
    user_list = [item[0] for item in user_data]

    list_asset_playbooks_data = phantom.collect2(
        container=container,
        datapath=[
            "list_asset_playbooks:custom_function_result.data.*.full_name"
        ])
    list_asset_playbooks_list = [
        item[0] for item in list_asset_playbooks_data if item[0]
    ]
    list_identity_playbooks_data = phantom.collect2(
        container=container,
        datapath=[
            "list_identity_playbooks:custom_function_result.data.*.full_name"
        ])
    list_identity_playbooks_list = [
        item[0] for item in list_identity_playbooks_data if item[0]
    ]

    #responses:
    all_entity_list = []
    response_types = []
    # only add a response if a device exists and a playbook exists
    if device_list and list_asset_playbooks_list:
        for item in device_list:
            if item:
                response_types.append({
                    "prompt":
                    "Launch protect asset playbooks on '{}'?".format(item),
                    "options": {
                        "type": "list",
                        "choices": ["Yes", "No"]
                    },
                })
                all_entity_list.append({'type': 'device', 'name': item})

    # only add a response if a user exists and a playbook exists
    if user_list and list_identity_playbooks_list:
        for item in user_list:
            if item:
                response_types.append({
                    "prompt":
                    "Launch protect identity playbooks on '{}'?".format(item),
                    "options": {
                        "type": "list",
                        "choices": ["Yes", "No"]
                    },
                })
                all_entity_list.append({'type': 'user', 'name': item})

    phantom.save_run_data(key='all_entities',
                          value=json.dumps(all_entity_list))
    phantom.prompt2(container=container,
                    user=user,
                    message=message,
                    respond_in_mins=30,
                    name="protect_prompt",
                    parameters=parameters,
                    response_types=response_types,
                    callback=decide_and_launch_playbooks)

    return
def workbook_task_update(task_name=None,
                         note_title=None,
                         note_content=None,
                         status=None,
                         owner=None,
                         container=None,
                         **kwargs):
    """
    Update a workbook task by task name
    
    Args:
        task_name (CEF type: *): Name of a workbook task (Required)
        note_title (CEF type: *): Note title goes here (Optional)
        note_content (CEF type: *): Body of note goes here (Optional)
        status (CEF type: *): One of: incomplete, in_progress, complete (Optional)
        owner (CEF type: *): Assigns task to provided owner. Accepts keyword 'current" to assign task to currently running playbook user. (Optional)
        container (CEF type: phantom container id): ID of Phantom Container (Required)
    
    Returns a JSON-serializable object that implements the configured data paths:
        note_id: Returns note_id if a note was added
    """
    ############################ Custom Code Goes Below This Line #################################
    import json
    import phantom.rules as phantom

    outputs = {}

    # Ensure valid container input
    if isinstance(container, dict) and container.get('id'):
        container_id = container['id']
    elif isinstance(container, int):
        container_id = container
    else:
        raise TypeError(
            "The input 'container' is neither a container dictionary nor an int, so it cannot be used"
        )

    if task_name:
        task_list = phantom.get_tasks(container_id)
        task_count = 0
        for task in task_list:
            if task_name == task['data']['name']:
                task_count += 1
                if task_count > 1:
                    raise RuntimeError(
                        f'Unable to update workbook task - multiple tasks match criteria: {task_count}'
                    )
                task_id = task['data']['id']
                task_is_note_required = task['data']['is_note_required']
                task_count += 1
                task_status = task['data']['status']
                task_notes = task['data']['notes']
                task_owner = task['data']['owner']

    if task_count == 0:
        raise RuntimeError(
            f"No task name matches input task_name: '{task_name}'")

    if task_is_note_required and (
            not note_content
            or not note_title) and status == 'complete' and task_status != 1:
        raise RuntimeError(
            'Unable to update workbook task - The task requires a closing note and a closing title'
        )
    else:
        # Add Note
        if note_content:
            success, message, note_id = phantom.add_note(
                container=container_id,
                note_type='task',
                task_id=task_id,
                title=note_title,
                content=note_content,
                note_format='markdown')
            outputs['note_id'] = str(note_id)

        # Set owner
        if owner:
            owner_dict = {}
            # If keyword 'current' entered then translate effective_user id to a username
            if owner.lower() == 'current':
                owner_dict['owner_id'] = phantom.get_effective_user()
            else:
                # Attempt to translate name to owner_id
                url = phantom.build_phantom_rest_url(
                    'ph_user') + f'?_filter_username="******"'
                data = phantom.requests.get(url,
                                            verify=False).json().get('data')
                if data and len(data) == 1:
                    owner_dict['owner_id'] = data[0]['id']
                elif data and len(data) > 1:
                    raise RuntimeError(f'Multiple matches for owner "{owner}"')
                else:
                    # Attempt to translate name to role_id
                    url = phantom.build_phantom_rest_url(
                        'role') + f'?_filter_name="{owner}"'
                    data = phantom.requests.get(
                        url, verify=False).json().get('data')
                    if data and len(data) == 1:
                        owner_dict['role_id'] = data[0]['id']
                    elif data and len(data) > 1:
                        raise RuntimeError(
                            f'Multiple matches for owner "{owner}"')
                    else:
                        raise RuntimeError(
                            f'"{owner}" is not a valid username or role')

            url = phantom.build_phantom_rest_url(
                'workbook_task') + '/{}'.format(task_id)
            response = phantom.requests.post(url,
                                             data=json.dumps(owner_dict),
                                             verify=False).json()
            if not response.get('success'):
                raise RuntimeError(f'Error setting "{owner}" - {response}')

        # Set Status
        if isinstance(status, str):
            status = status.lower()
            url = phantom.build_phantom_rest_url(
                'workbook_task') + '/{}'.format(task_id)
            if status == 'complete' and task_status == 0:
                # Move to in progress
                data = {'status': 2}
                response = phantom.requests.post(url,
                                                 data=json.dumps(data),
                                                 verify=False).json()
                if not response.get('success'):
                    raise RuntimeError(
                        f'Error setting status "{status}" - {response}')
                    # Then move to close
                data = {'status': 1}
                if task_is_note_required and note_content:
                    data['note'] = note_content
                    data['title'] = note_title
                    data['note_format'] = 'markdown'
                response = phantom.requests.post(url,
                                                 data=json.dumps(data),
                                                 verify=False).json()
                if not response.get('success'):
                    raise RuntimeError(
                        f'Error setting status "{status}" - {response}')
            elif (status == 'in progress'
                  or status == 'in_progress') and task_status != 2:
                data = {'status': 2}
                # Move to in progress
                response = phantom.requests.post(url,
                                                 data=json.dumps(data),
                                                 verify=False).json()
                if not response.get('success'):
                    raise RuntimeError(
                        f'Error setting status "{status}" - {response}')
            elif status == 'incomplete' and task_status != 0:
                data = {'status': 0}
                # Move to incomplete
                response = phantom.requests.post(url,
                                                 data=json.dumps(data),
                                                 verify=False).json()
                if not response.get('success'):
                    raise RuntimeError(
                        f'Error setting status "{status}" - {response}')
            elif status == 'complete' and task_status != 1:
                data = {'status': 1}
                # Move to complete
                if task_is_note_required and note_content:
                    data['note'] = note_content
                    data['title'] = note_title
                    data['note_format'] = 'markdown'
                response = phantom.requests.post(url,
                                                 data=json.dumps(data),
                                                 verify=False).json()
                if not response.get('success'):
                    raise RuntimeError(
                        f'Error setting status "{status}" - {response}')

    # Return a JSON-serializable object
    assert json.dumps(
        outputs
    )  # Will raise an exception if the :outputs: object is not JSON-serializable
    return outputs