CyrenThreatInDepthRenderRelated

Shows feed relationship data in a table with the ability to navigate.

python · Cyren Threat InDepth Threat Intelligence

Source

import json
from datetime import datetime
from enum import Enum

import demistomock as demisto
from CommonServerPython import *

from CommonServerUserPython import *

SCORE_TO_REPUTATION_TEXT = {
    Common.DBotScore.NONE: f"None ({Common.DBotScore.NONE})",
    Common.DBotScore.GOOD: f"Good ({Common.DBotScore.GOOD})",
    Common.DBotScore.SUSPICIOUS: f"Suspicious ({Common.DBotScore.SUSPICIOUS})",
    Common.DBotScore.BAD: f"Bad ({Common.DBotScore.BAD})",
}


class AcceptedHeader(str, Enum):
    INDICATOR_TYPE = "Indicator Type"
    VALUE = "Value"
    REPUTATION = "Reputation"
    RELATIONSHIP_TYPE = "Relationship Type"
    ENTITY_CATEGORY = "Entity Category"
    TIMESTAMP = "Timestamp UTC"


ACCEPTED_HEADERS = [
    AcceptedHeader.INDICATOR_TYPE,
    AcceptedHeader.VALUE,
    AcceptedHeader.REPUTATION,
    AcceptedHeader.RELATIONSHIP_TYPE,
    AcceptedHeader.ENTITY_CATEGORY,
    AcceptedHeader.TIMESTAMP,
]


def check_acceptable_headers(headers):
    for header in headers:
        if header not in ACCEPTED_HEADERS:
            raise ValueError(f"Please provide columns from {ACCEPTED_HEADERS}!")


def create_relationship_object(
    value: str,
    relationship_type: str,
    indicator_type: str,
    timestamp: str,
    entity_category: str,
    reputation: int = Common.DBotScore.NONE,
):
    try:
        timestamp_parsed = datetime.fromisoformat(timestamp.replace("Z", "+00:00"))
        timestamp_human_readable = timestamp_parsed.strftime("%Y-%m-%d, %H:%M:%S")
    except ValueError:
        demisto.info("could not parse timestamp %s, keeping original string", timestamp)
        timestamp_human_readable = timestamp

    indicator_object = {
        "Value": f"{value}\n",
        "Reputation": SCORE_TO_REPUTATION_TEXT[reputation],
        "Relationship Type": relationship_type,
        "Indicator Type": indicator_type,
        "Timestamp UTC": timestamp_human_readable,
        "Entity Category": entity_category,
    }

    return indicator_object


def cyren_feed_relationship(args) -> CommandResults:
    if "indicator" not in args:
        raise ValueError("Please provide 'indicator' argument!")

    indicator = args["indicator"]
    if isinstance(indicator, str):
        try:
            indicator = json.loads(indicator)
        except json.JSONDecodeError:
            raise ValueError("Please provide JSON-encoded 'indicator' param!")
    elif not isinstance(indicator, dict):
        raise ValueError("Please provide JSON-encoded 'indicator' param!")

    headers = args.get("columns", ACCEPTED_HEADERS)
    if isinstance(headers, str):
        headers = [s.strip() for s in headers.split(",")]

    check_acceptable_headers(headers)

    relationships = indicator.get("CustomFields", {}).get("cyrenfeedrelationships", []) or []

    content = []

    for item in relationships:
        ioc_value = item.get("value", "")
        search_indicators = IndicatorsSearcher()

        results = search_indicators.search_indicators_by_version(value=ioc_value).get("iocs", [])

        if results:
            result = results[0]
            ioc_score = result.get("score")
            ioc_id = result.get("id")

            content.append(
                create_relationship_object(
                    value=f"[{ioc_value}](#/indicator/{ioc_id})" if ioc_value else "",
                    relationship_type=item.get("relationshiptype"),
                    indicator_type=item.get("indicatortype"),
                    timestamp=item.get("timestamp"),
                    entity_category=item.get("entitycategory"),
                    reputation=ioc_score,
                )
            )

        else:
            # In case that no related indicators were found, return the table without the link.
            content.append(
                create_relationship_object(
                    value=ioc_value,
                    relationship_type=item.get("relationshiptype"),
                    indicator_type=item.get("indicatortype"),
                    timestamp=item.get("timestamp"),
                    entity_category=item.get("entitycategory"),
                )
            )

    output = tableToMarkdown("", content, headers, removeNull=True)
    return CommandResults(readable_output=output)


def main(args):
    try:
        return_results(cyren_feed_relationship(args))
    except Exception as e:
        return_error(f"Failed to execute CyrenThreatInDepthRenderRelated. Error: {e!s}")


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

README

Widget script to view information about the relationship information the Cyren Threat InDepth
feeds offer. For instance, you can see and navigate to a malicious SHA256 that was hosted by
a malicious URL.

The script provides base functionality for other scripts that are supposed to be used similar
to the Feed Related Indicators.

Script Data


Name Description
Script Type python3
Tags cyren
XSOAR Version 6.0.0

Inputs


Argument Name Description
indicator JSON representation of the indicator holding the relationship data.
columns Optional Comma-separated column list (for instance Value,Indicator Type). If not provided, the full list of supported columns is assumed.

Outputs


There are no outputs for this script.

Human Readable Output


Indicator Type Value Reputation Relationship Type Entity Category Timestamp UTC
IP 172.217.6.65 None (0) resolves to malware 2021-01-07, 09:02:21
SHA-256 6ea626950a759c259a182b628f79816843af379af87dbbe13923bf72d6047770 Bad (3) serves malware 2021-01-07, 09:02:21