Ejemplo n.º 1
0
def handle_state(execution_context, entries: Any):
    """
    This integration helps to cache, return and (optionally) re-map fields of data so that it can be used by other States. Choice states particularly benefit from this integration as
    Choice states can only refer the return results that are one step before it. This cache function can be used to pull data from earlier states not immediately preceeding the choice state so that it can be used by Choice States.
    The integration can also be used to re-map result fields from other integrations.

    Args:
        "entries": Any data you want to cache.

    Returns:
        A dictionary that contains the thing you want to be cached.
        {"cached": data}
    """
    if isinstance(entries, str):
        entries = socless_template_string(entries, execution_context)
    elif isinstance(entries, dict):
        new_entries = {}
        for key, value in list(entries.items()):
            if isinstance(value, str):
                new_entries[key] = socless_template_string(
                    value, execution_context)
            else:
                new_entries[key] = value
        entries = new_entries
    return {"cached": entries}
Ejemplo n.º 2
0
def handle_state(
    context,
    message_template: str,
    target: str,
    target_type: str,
    as_user: bool = True,
    token: str = "",
):
    """Send a Slack message without expecting a response.
    Args:
        message_template : A string to send to the target. SOCless template
            notation will unpack any references in the string
        target           : the username or slack id to send this message to
        target_type      : "slack_id" | "user" | "channel"
        token            : you can pass an alternate token via Jinja template in playbook.json (ssm, environment, etc)
    """
    helper = SlackHelper(token)

    if not all([target_type, target, message_template]):
        raise Exception(
            "Incomplete parameters supplied. Please supply target, target_type and message_template"
        )

    message = socless_template_string(message_template, context)

    resp = helper.slack_post_msg_wrapper(target,
                                         target_type,
                                         text=message,
                                         as_user=as_user)

    return {"response": resp.data, "slack_id": resp["channel"]}  # type: ignore
Ejemplo n.º 3
0
def handle_state(
    context,
    target: str,
    target_type: str,
    message_template: str,
    receiver: str = "",
    response_desc: str = "[response]",
    as_user: bool = True,
    token: str = "",
):
    """Send a Slack Message and store the message id for the message.
    Args:
        target         : the username or slack id to send this message to
        target_type    : "slack_id" | "user" | "channel"
        token          : you can pass an alternate token via Jinja template in playbook.json (ssm, environment, etc)
    Returns:

    """
    helper = SlackHelper(token)

    if not message_template:
        raise Exception("No text was supplied to Slack message")

    USE_NEW_INTERACTION = "task_token" in context

    message_id = gen_id(6)

    context["_message_id"] = message_id
    extended_template = "{message_template}\n```{slash_command} {context[_message_id]} {response_desc}```\n".format(
        message_template=message_template,
        slash_command=SLACK_SLASH_COMMAND,
        context=context,
        response_desc=response_desc,
    )
    message = socless_template_string(extended_template, context)

    if USE_NEW_INTERACTION:
        init_human_interaction(context, message, message_id)

    resp = helper.slack_post_msg_wrapper(target,
                                         target_type,
                                         text=message,
                                         as_user=as_user)

    if not USE_NEW_INTERACTION:
        investigation_id = context["artifacts"]["event"]["investigation_id"]
        execution_id = context.get("execution_id")
        socless_dispatch_outbound_message(receiver, message_id,
                                          investigation_id, execution_id,
                                          message)

    return {
        "response": resp.data,  # type: ignore
        "message_id": message_id,
        "slack_id": resp["channel"],  # type: ignore
    }
Ejemplo n.º 4
0
def handle_state(context,
                 target,
                 target_type,
                 message_template,
                 receiver="",
                 response_desc='[response]'):
    """
    Send a Slack Message and store the message id for the message
    """

    if not message_template:
        raise Exception("No text was supplied to Slack message")

    USE_NEW_INTERACTION = 'task_token' in context

    message_id = gen_id(6)

    context['_message_id'] = message_id
    extended_template = "{message_template}\n```{slash_command} {context[_message_id]} {response_desc}```\n".format(
        message_template=message_template,
        slash_command=SLACK_SLASH_COMMAND,
        context=context,
        response_desc=response_desc)
    message = socless_template_string(extended_template, context)
    target_id = get_channel_id(target, target_type)

    if USE_NEW_INTERACTION:
        init_human_interaction(context, message, message_id)

    r = slack_client.chat_postMessage(channel=target_id,
                                      text=message,
                                      as_user=True)

    if not r.data['ok']:
        raise Exception(
            f"Human Reponse workflow failed to initiate because slack_client failed to send message: {r.data}"
        )

    if not USE_NEW_INTERACTION:
        investigation_id = context['artifacts']['event']['investigation_id']
        execution_id = context.get('execution_id')
        socless_dispatch_outbound_message(receiver, message_id,
                                          investigation_id, execution_id,
                                          message)

    return {
        "response": r.data,
        "message_id": message_id,
        "slack_id": target_id
    }
Ejemplo n.º 5
0
def handle_state(context, target, target_type, message_template):
    """
    Send a Slack message to either a user or channel
    """
    target_id = ''
    # Determine Slack ID for the target
    if target_type == 'user':
        result = find_user(target)
        if result['found'] == False:
            raise Exception(f"User {target} not found in Slack instance")

        target_id = result['user']['id']
    else:
        target_id = f"#{target}"

    # Render the message template and send the message
    message = socless_template_string(message_template, context)
    resp = sc.chat_postMessage(channel=target_id, text=message, as_user=True)
    return resp.data
Ejemplo n.º 6
0
def handle_state(
    context,
    target: str,
    target_type: str,
    content: Optional[str] = "",
    file: Optional[str] = "",
    filename: Optional[str] = "",
    title: Optional[str] = "",
    initial_comment: Optional[str] = "",
    token: Optional[str] = "",
    **kwargs,
):
    """Send a Slack message without expecting a response https://api.slack.com/methods/files.upload
    Args:
        content : Provide EITHER `content` OR `file`. CONTENT: A string
        file    : Provide EITHER `content` OR `file`. FILE: file path pointing to location in the lambda's local environment.
        filename: Filename of the file.
        initial_comment : The message text introducing the file.
        title : Title of the file
        target           : the username or slack id to send this message to
        target_type      : "slack_id" | "user" | "channel"
        token            : you can pass an alternate token via Jinja template in playbook.json (ssm, environment, etc)
    """
    helper = SlackHelper(token)

    if all([content, file]) or not any([content, file]):
        raise SlackError(
            "Please provide EITHER a `file` OR a `content` argument.")

    if not all([target_type, target]):
        raise SlackError(
            "Incomplete parameters supplied. Please `supply target`, `target_type`"
        )

    content = socless_template_string(content, context)

    slack_target = helper.resolve_slack_target(target, target_type)

    # NOTE: this api supports multiple targets, with channel as a comma separated list
    file_upload_args = {"channels": slack_target}

    if content:
        file_upload_args["content"] = content
    if file:
        file_upload_args["file"] = file
    if filename:
        file_upload_args["filename"] = filename
    if initial_comment:
        file_upload_args["initial_comment"] = initial_comment
    if title:
        file_upload_args["title"] = title

    file_upload_args = {**file_upload_args, **kwargs}

    resp = helper.client.files_upload(**file_upload_args)

    if not resp.data["ok"]:
        raise SlackError(
            f"Slack error during file_upload to {target}: {resp.data['error']}"
        )

    return {"response": resp.data, "slack_id": slack_target}  # type: ignore
Ejemplo n.º 7
0
def handle_state(
    context,
    target_type: str,
    target: str,
    text: str,
    receiver: str = "",
    prompt_text: str = "",
    yes_text: str = "Yes",
    no_text: str = "No",
    as_user: bool = True,
    token: str = "",
):
    """Send a Slack Message and store the message id for the message.
    Args:
        target         : the username or slack id to send this message to
        target_type    : "slack_id" | "user" | "channel"
        token          : you can pass an alternate token via Jinja template in playbook.json (ssm, environment, etc)
    Returns:

    """
    helper = SlackHelper(token)
    USE_NEW_INTERACTION = "task_token" in context

    if not all([target_type, target, text]):
        raise Exception(
            "Incomplete inputs: target, target_type and text must be supplied")

    ATTACHMENT_YES_ACTION = {
        "name": "yes_text",
        "style": "default",
        "text": "",
        "type": "button",
        "value": "true",
    }
    ATTACHMENT_NO_ACTION = {
        "name": "no_text",
        "style": "danger",
        "text": "",
        "type": "button",
        "value": "false",
    }

    ATTACHMENT_TEMPLATE = {
        "text": "",
        "mrkdwn_in": ["text"],
        "fallback": "New message",
        "callback_id": "",
        "color": "#3AA3E3",
        "attachment_type": "default",
        "actions": [],
    }

    message_id = gen_id(6)
    context["_message_id"] = message_id
    text = socless_template_string(text, context)
    prompt_text = socless_template_string(prompt_text, context)

    ATTACHMENT_TEMPLATE["text"] = "*{}*".format(prompt_text)
    ATTACHMENT_TEMPLATE["callback_id"] = message_id
    ATTACHMENT_YES_ACTION["text"] = yes_text
    ATTACHMENT_NO_ACTION["text"] = no_text
    ATTACHMENT_TEMPLATE["actions"] = [
        ATTACHMENT_YES_ACTION, ATTACHMENT_NO_ACTION
    ]

    payload = {"text": text, "ATTACHMENT_TEMPLATE": ATTACHMENT_TEMPLATE}

    if USE_NEW_INTERACTION:
        init_human_interaction(context, payload, message_id)

    resp = helper.slack_post_msg_wrapper(
        target,
        target_type,
        text=text,
        attachments=[ATTACHMENT_TEMPLATE],
        as_user=as_user,
    )

    if not USE_NEW_INTERACTION:
        investigation_id = context["artifacts"]["event"]["investigation_id"]
        execution_id = context.get("execution_id")
        socless_dispatch_outbound_message(receiver, message_id,
                                          investigation_id, execution_id,
                                          payload)

    return {
        "response": resp.data,  # type: ignore
        "message_id": message_id,
        "slack_id": resp["channel"],  # type: ignore
    }
Ejemplo n.º 8
0
    def handle_state(
        context: dict,
        investigation_escalated: bool,
        findings: str,
        metadata: dict = {},
    ):
        """
        Create a log file and upload it to SOCless logging bucket.

        Args:
            investigation_escalated (bool) : Did the investigation require handoff to a human in order to complete?
            findings (str): The investigation's findings or outcome state.
            metadata (obj) Any additional info for logging. Any variable type that's JSON serializable

        Env:
            SOCLESS_Logs (str): The name of the bucket where you want to upload logs to

        Returns:
            A dict containing the file_id (S3 Object path) and vault_id (Socless logging bucket
            reference) of the saved content
        """
        bucket_name = os.environ.get("SOCLESS_Logs")
        log_type = "findings"
        log_source = ctx.invoked_function_arn
        aws_region = log_source.split(":")[3]
        aws_account = log_source.split(":")[4]
        event_type = context["artifacts"]["event"]["event_type"]
        playbook_name = context["artifacts"]["event"]["playbook"]
        execution_id = context["execution_id"]
        execution_arn = "arn:aws:states:{}:{}:execution:{}:{}".format(
            aws_region, aws_account, playbook_name, execution_id)
        investigation_id = context["artifacts"]["event"]["investigation_id"]
        event_payload = context["artifacts"]["event"]["details"]
        utc_time = datetime.utcnow()
        utc_time_iso = utc_time.isoformat() + "Z"
        year = utc_time.year
        month = utc_time.month
        day = utc_time.day
        uuid = gen_id()
        file_id = "{}/{}/{}/{}/{}/{}.json".format(log_type, year, month, day,
                                                  playbook_name, uuid)
        log = {
            "log_type": log_type,
            "log_source": log_source,
            "timestamp": utc_time_iso,
            "execution_arn": execution_arn,
            "investigation_id": investigation_id,
            "event_type": event_type,
            "event_payload": event_payload,
            "investigation_escalated": investigation_escalated,
            "metadata": metadata,
        }

        try:
            findings = socless_template_string(urllib.parse.unquote(findings),
                                               context)
        except Exception as e:
            print(
                f"unable to parse socless_template_string. Error: {e}. Findings: {findings}"
            )
            raise

        log["findings"] = findings

        return save_to_s3(file_id, log, bucket_name, False)
Ejemplo n.º 9
0
def handle_state(context,
                 target_type,
                 target,
                 text,
                 receiver='',
                 prompt_text='',
                 yes_text='Yes',
                 no_text='No'):
    """
    Send a Slack Message and store the message id for the message
    """
    USE_NEW_INTERACTION = 'task_token' in context

    if not all([target_type, target, text]):
        raise Exception(
            "Incomplete inputs: target, target_type and text must be supplied")
    target_id = get_channel_id(target, target_type)

    ATTACHMENT_YES_ACTION = {
        "name": "yes_text",
        "style": "default",
        "text": "",
        "type": "button",
        "value": "true"
    }
    ATTACHMENT_NO_ACTION = {
        "name": "no_text",
        "style": "danger",
        "text": "",
        "type": "button",
        "value": "false"
    }

    ATTACHMENT_TEMPLATE = {
        "text": "",
        "mrkdwn_in": ["text"],
        "fallback": "New message",
        "callback_id": "",
        "color": "#3AA3E3",
        "attachment_type": "default",
        "actions": []
    }

    message_id = gen_id(6)
    context['_message_id'] = message_id
    text = socless_template_string(text, context)
    prompt_text = socless_template_string(prompt_text, context)

    ATTACHMENT_TEMPLATE['text'] = "*{}*".format(prompt_text)
    ATTACHMENT_TEMPLATE['callback_id'] = message_id
    ATTACHMENT_YES_ACTION['text'] = yes_text
    ATTACHMENT_NO_ACTION['text'] = no_text
    ATTACHMENT_TEMPLATE['actions'] = [
        ATTACHMENT_YES_ACTION, ATTACHMENT_NO_ACTION
    ]

    payload = {"text": text, "ATTACHMENT_TEMPLATE": ATTACHMENT_TEMPLATE}

    if USE_NEW_INTERACTION:
        init_human_interaction(context, payload, message_id)

    resp = slack_client.chat_postMessage(channel=target_id,
                                         text=text,
                                         attachments=[ATTACHMENT_TEMPLATE],
                                         as_user=True)

    if not resp.data['ok']:
        raise Exception(resp.data['error'])

    if not USE_NEW_INTERACTION:
        investigation_id = context['artifacts']['event']['investigation_id']
        execution_id = context.get('execution_id')
        socless_dispatch_outbound_message(receiver, message_id,
                                          investigation_id, execution_id,
                                          payload)

    return {
        'response': resp.data,
        "message_id": message_id,
        "slack_id": target_id
    }