BreachRx

Automate your privacy Incident Response workflow through the BreachRx platform.

Case Management · BreachRx

Details

IDBreachRx
ProviderBreachRx
CategoryCase Management
From Version6.2.0
Docker Imagedemisto/graphql:1.0.0.10120494
Supported ModulesAgentix XSIAM

README

Automate your privacy Incident Response workflow through the BreachRx platform.
This integration was integrated and tested with version 1.20 of BreachRx

Configure BreachRx in Cortex

Parameter Description Required
Your BreachRx URL   True
GraphQL API URL   True
API Key The API Key to use for connection True
Secret Key The API Key to use for connection True
Use system proxy settings   False

Commands

You can execute these commands from the CLI, as part of an automation, or in a playbook.
After you successfully execute a command, a DBot message appears in the War Room with the command details.

breachrx-incident-create


Create a privacy Incident on the BreachRx platform.

Base Command

breachrx-incident-create

Input

Argument Name Description Required
incident_name The name to use when creating the privacy Incident on the BreachRx platform. Optional
description A brief description to explain the privacy Incident on the BreachRx platform. Optional

Context Output

Path Type Description
BreachRx.Incident.id string The ID of the privacy Incident on the BreachRx platform.
BreachRx.Incident.name string The name of the privacy Incident on the BreachRx platform.
BreachRx.Incident.description string The description of the privacy Incident on the BreachRx platform.
BreachRx.Incident.identifier string The unique identifier of the privacy Incident on the BreachRx platform.

breachrx-incident-actions-get


Fetch all actions for a BreachRx privacy Incident.

Base Command

breachrx-incident-actions-get

Input

Argument Name Description Required
incident_name The name of the BreachRx incident to associate with this incident. Optional
incident_identifier The unique identifier of the BreachRx incident to associate with this incident. Optional

Context Output

Path Type Description
BreachRx.Incident.Actions.id string The ID of an Action on a BreachRx privacy Incident.
BreachRx.Incident.Actions.name string The name of an Action on a BreachRx privacy Incident.
BreachRx.Incident.Actions.description string The description of an Action on a BreachRx privacy Incident.
BreachRx.Incident.Actions.user.email string The email of the assigned user of an Action on a BreachRx privacy Incident, if that Action is assigned to a user (null otherwise).

breachrx-incident-import


Link a BreachRx privacy Incident with an XSOAR Incident.

Base Command

breachrx-incident-import

Input

Argument Name Description Required
incident_name The name of the BreachRx incident to associate with this incident. Optional
incident_identifier The unique identifier of the BreachRx incident to associate with this incident. Optional

Context Output

Path Type Description
BreachRx.Incident.id string The ID of the privacy Incident on the BreachRx platform.
BreachRx.Incident.name string The name of the privacy Incident on the BreachRx platform.
BreachRx.Incident.identifier string The unique identifier of the privacy Incident on the BreachRx platform.

breachrx-incident-get


Find a BreachRx privacy Incident on the connected BreachRx platform.

Base Command

breachrx-incident-get

Input

Argument Name Description Required
incident_name The name of the BreachRx incident to associate with this incident. Optional
incident_identifier The unique identifier of the BreachRx incident to associate with this incident. Optional

Context Output

Path Type Description
BreachRx.Incident.id string The ID of the privacy Incident on the BreachRx platform.
BreachRx.Incident.name string The name of the privacy Incident on the BreachRx platform.
BreachRx.Incident.identifier string The unique identifier of the privacy Incident on the BreachRx platform.

Configuration parameters

  • url — Your BreachRx URL (required)
  • api_url — GraphQL API URL (required)
  • credentials — API Key (required)
  • proxy — Use system proxy settings
  • insecure — Trust any certificate (not secure)

Commands (4)

  • breachrx-incident-actions-get

    Fetch all actions for a BreachRx privacy Incident.

  • breachrx-incident-create

    Create a privacy Incident on the BreachRx platform.

  • breachrx-incident-get

    Find a BreachRx privacy Incident on the connected BreachRx platform.

  • breachrx-incident-import

    Link a BreachRx privacy Incident with an XSOAR Incident.

from CommonServerPython import *  # noqa # pylint: disable=unused-wildcard-import
from CommonServerUserPython import *  # noqa
from collections.abc import Callable

import traceback
from urllib.parse import urlparse

from gql import gql, Client
from gql.transport.requests import RequestsHTTPTransport
from requests.auth import HTTPBasicAuth


create_incident_mutation = gql("""
mutation CreateIncident(
  $severity: String!,
  $name: String!,
  $type: String!,
  $description: String
) {
  createIncident(
    type: $type,
    severity: $severity,
    name: $name,
    description: $description
  ) {
    id
    name
    severity {
      name
    }
    types {
      type {
        name
      }
    }
    description
    identifier
  }
}""")

get_incident_severities = gql("""{
  incidentSeverities {
    name
    ordering
  }
}""")

get_incident_types = gql("""{
  types {
    name
  }
}""")

get_actions_for_incident = gql("""
query GetActionsForIncident($incidentId: Int!) {
  actions(where: {
    incidentId: {
      equals: $incidentId
    }
  }) {
    id
    name
    description
    phase {
      name
      id
    }
    user {
      fullName
      email
    }
  }
}""")

get_incident_by_name = gql("""
query GetIncidentByName($name: String, $identifier: String) {
  incidents(first: 1, where: {
    name: {
      contains: $name,
      mode: insensitive
    }
    identifier: {
      contains: $identifier,
      mode: insensitive
    }
  }) {
    id
    name
    severity {
      name
    }
    types {
      type {
        name
      }
    }
    description
    identifier
  }
}""")


class BreachRxClient:
    def __init__(self, base_url: str, api_key: str, secret_key: str, org_name: str, verify: bool):
        self.api_key = api_key
        self.secret_key = secret_key
        self.org_name = org_name

        auth = HTTPBasicAuth(api_key, secret_key)

        transport = RequestsHTTPTransport(url=base_url, auth=auth, headers={"orgname": org_name}, timeout=60, verify=verify)

        self.client = Client(transport=transport, fetch_schema_from_transport=False)

    def get_incident_severities(self):
        return self.client.execute(get_incident_severities)["incidentSeverities"]

    def get_incident_types(self):
        return self.client.execute(get_incident_types)["types"]

    def create_incident(self, name: Optional[str], description: Optional[str]):
        severities = self.get_incident_severities()
        types = self.get_incident_types()

        request = create_incident_mutation
        request.variable_values = {
            "severity": severities[0]["name"],
            "name": name,
            "type": types[0]["name"],
            "description": description,
        }
        return self.client.execute(request)["createIncident"]

    def get_incident(self, name: Optional[str], identifier: Optional[str]):
        request = get_incident_by_name
        request.variable_values = {"name": name, "identifier": identifier}
        results = self.client.execute(request)["incidents"]

        if results:
            return results.pop()
        else:
            return None

    def get_actions_for_incident(self, incident_id):
        request = get_actions_for_incident
        request.variable_values = {"incidentId": incident_id}

        return self.client.execute(request)["actions"]


def test_module(client: BreachRxClient):
    try:
        client.get_incident_severities()
        return "ok"
    except Exception:
        raise Exception("Authorization Error: make sure your API Key and Secret Key are correctly set")


def create_incident_command(
    client: BreachRxClient, incident_name: str | None = None, description: str | None = None
) -> CommandResults:
    if not incident_name:
        incident_name = demisto.incident().get("name")
    if not description:
        description = f"""An Incident copied from the Palo Alto Networks XSOAR platform.
            <br>
            <br>
            XSOAR Incident Name: {demisto.incident().get('name')}"""

    response = client.create_incident(incident_name, description)

    incident_name = response["name"]

    return CommandResults(
        outputs_prefix="BreachRx.Incident",
        outputs_key_field="id",
        outputs=response,
        raw_response=response,
        readable_output=f"Incident created with name={incident_name}.",
    )


def get_incident_actions_command(
    client: BreachRxClient, incident_name: str | None = None, incident_identifier: str | None = None
) -> Union[CommandResults, str]:
    incidents = demisto.dt(demisto.context(), "BreachRx.Incident")

    if not incidents:
        if not incident_name and not incident_identifier:
            raise Exception(
                "Error: No BreachRx privacy Incident associated with this Incident, and no Incident search terms provided."
            )

        incidents = [client.get_incident(incident_name, incident_identifier)]

        if not incidents:
            raise Exception("Error: No BreachRx privacy Incident found using the search terms provided.")

    if not isinstance(incidents, list):
        incidents = [incidents]

    for incident in incidents:
        incident["actions"] = client.get_actions_for_incident(incident["id"])

        for action in incident["actions"]:
            action["phase_name"] = action["phase"]["name"]

    readable_output = ""

    for incident in incidents:
        actions_markdown_table = tableToMarkdown("Actions", incident["actions"], headers=["name", "phase_name"])
        readable_output += f"# {incident['name']} ({incident['id']})\n" + actions_markdown_table + "\n"

    return CommandResults(
        outputs_prefix="BreachRx.Incident",
        outputs_key_field="id",
        outputs=incidents,
        raw_response=incidents,
        readable_output=readable_output,
    )


def import_incident_command(
    client: BreachRxClient, incident_name: str | None = None, incident_identifier: str | None = None
) -> Union[CommandResults, str]:
    incident = client.get_incident(incident_name, incident_identifier)

    if not incident:
        raise Exception("Error: No BreachRx privacy Incident found using the search terms provided.")

    return CommandResults(
        outputs_prefix="BreachRx.Incident",
        outputs_key_field="id",
        outputs=incident,
        raw_response=incident,
        readable_output=f"Incident imported with name={incident.get('name')}.",
    )


def get_incident_command(
    client: BreachRxClient, incident_name: str | None = None, incident_identifier: str | None = None
) -> Union[CommandResults, str]:
    incident = client.get_incident(incident_name, incident_identifier)

    if incident:
        return CommandResults(
            raw_response=incident,
            readable_output=f'Incident found with name="{incident.get("name")}" and identifier="{incident.get("identifier")}".',
        )
    else:
        return "No Incident found with those search terms."


COMMANDS = {
    "test-module": test_module,
    "breachrx-incident-create": create_incident_command,
    "breachrx-incident-actions-get": get_incident_actions_command,
    "breachrx-incident-import": import_incident_command,
    "breachrx-incident-get": get_incident_command,
}


def is_valid_url(url: str):
    try:
        result = urlparse(url)
        return all([result.scheme, result.netloc])
    except ValueError:
        return False


def main() -> None:  # pragma: no cover
    try:
        params = demisto.params()
        args = demisto.args()
        command = demisto.command()

        base_url = params["api_url"]
        org_name = params["url"].split(".")[0].replace("https://", "")
        api_key = params.get("credentials", {}).get("identifier")
        secret_key = params.get("credentials", {}).get("password")
        verify = params.get("insecure", False)

        if not is_valid_url(base_url):
            raise Exception("The GraphQL API URL is not a valid URL.")

        if not is_valid_url(params["url"]):
            raise Exception("The BreachRx instance URL is not a valid URL.")

        client = BreachRxClient(base_url, api_key, secret_key, org_name, verify)

        command_func: Any[Callable, None] = COMMANDS.get(command)

        if command_func:
            return_results(command_func(client, **args))
        else:
            raise NotImplementedError(f"{command} command is not implemented.")
    except Exception as e:
        demisto.error(traceback.format_exc())
        return_error(f"Failed to execute {command} command.\nError:\n{e!s}")


if __name__ in ("__main__", "__builtin__", "builtins"):
    main()