Darkmon Feed

Darkmon TIP indicator feed for Cortex XSOAR. Pulls IPs, URLs, domains, file hashes, emails, and accounts from the Darkmon Threat Intel firehose into the XSOAR TIM module. Pair with the Darkmon integration for incident fetching and automation commands.

Data Enrichment & Threat Intelligence · Darkmon · Feed

Details

IDDarkmon Feed
ProviderDarkmon
CategoryData Enrichment & Threat Intelligence
From Version6.8.0
Docker Imagedemisto/python3:3.12.13.10116658
Supported ModulesCloud Posture Security Cortex Cloud Cloud Runtime Security EDR Attack Surface Management Threat Intelligence Management Application Security XSIAM Exposure Management Agentix Email Security

README

Darkmon TIP indicator feed for Cortex XSOAR. Pulls IPs, URLs, domains, file hashes, emails, and accounts from the Darkmon Threat Intel firehose into the Cortex XSOAR Threat Intelligence Module (TIM). Pair with the Darkmon integration for incident fetching and automation commands.

Configure Darkmon Feed in Cortex

Parameter Description Required
API Base URL Override the Darkmon TIP API base URL only if your tenant points at a non-default endpoint. The default value already targets the production Darkmon TIP service. Leave blank to use the default. False
API key   True
Trust any certificate (not secure)   False
Use system proxy settings   False
Fetch indicators   False
Indicator Reputation Indicators from this integration instance will be marked with this reputation. False
Source Reliability Reliability of the source providing the intelligence data. True
Feed Fetch Interval   False
Bypass exclusion list When selected, the exclusion list is ignored for indicators from this feed. False
Traffic Light Protocol Color The Traffic Light Protocol (TLP) designation to apply to indicators fetched from the feed. False
Tags Supports CSV values. False
Indicator fetch limit Maximum number of indicators to fetch per cycle. 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.

darkmon-get-indicators


Fetch a page of Darkmon indicators on demand without waiting for the next scheduled feed cycle. Useful for debugging.

Base Command

darkmon-get-indicators

Input

Argument Name Description Required
limit Maximum number of indicators to return. Default is 20. Optional

Context Output

There is no context output for this command.

Configuration parameters

  • base_url — API Base URL
  • X-API-KEY — (required)
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings
  • feed — Fetch indicators
  • feedReputation — Indicator Reputation
  • feedReliability — Source Reliability (required)
  • feedExpirationPolicy
  • feedExpirationInterval
  • feedFetchInterval — Feed Fetch Interval
  • feedBypassExclusionList — Bypass exclusion list
  • tlp_color — Traffic Light Protocol Color
  • feedTags — Tags
  • limit — Indicator fetch limit

Commands (1)

  • darkmon-get-indicators

    Fetch a page of Darkmon indicators on demand without waiting for the next scheduled feed cycle. Useful for debugging.

"""Darkmon Feed integration - indicator firehose ingest.

Split out of the main Darkmon integration to match the Cortex content
convention: feed: true and isfetch: true live in separate integrations
within the same pack (see Anomali, RecordedFuture, ReversingLabs, etc.).

This module owns:
  - fetch-indicators (TIM feed pull, scheduled by feedFetchInterval)
  - darkmon-get-indicators (manual debug pull)
  - test-module (instance test)

The main Darkmon integration owns everything else (reputation,
dmontip-* automation, fetch-incidents).
"""

from typing import Any

import demistomock as demisto  # noqa: F401
import urllib3
from CommonServerPython import *  # noqa: F401
from urllib3.exceptions import InsecureRequestWarning

urllib3.disable_warnings(InsecureRequestWarning)

VENDOR = "Darkmon"
DEFAULT_BASE_URL = "https://api.darkmon.com/tip/2025.1"
DEFAULT_SIZE = 20

# Map Darkmon API indicator-type strings to XSOAR FeedIndicatorType values
FEED_INDICATOR_TYPE_MAP = {
    "IP": FeedIndicatorType.IP,
    "Domain": FeedIndicatorType.Domain,
    "URL": FeedIndicatorType.URL,
    "Email": FeedIndicatorType.Email,
    "File": FeedIndicatorType.File,
    "Account": FeedIndicatorType.Account,
}


class Client(BaseClient):
    """Minimal Darkmon API client for the feed half.

    Mirrors only the methods needed by fetch-indicators and the debug
    darkmon-get-indicators command. Mirrors the auth + transport behavior
    of the main Darkmon integration's Client so a single API key works
    against both integrations.
    """

    def __init__(self, base_url: str, headers: dict, verify: bool = True, proxy: bool = False):
        super().__init__(base_url=base_url, verify=verify, proxy=proxy, headers=headers)

    def validate_api_key(self) -> bool:
        self._http_request("GET", url_suffix="/health", ok_codes=(200,))
        return True

    def get_indicators(self, size: int = DEFAULT_SIZE) -> dict:
        return self._http_request(
            "GET",
            url_suffix="/indicators",
            params={"size": size},
        )


def _apply_indicator_fields(item: dict, ioc_type: str, indicator_obj: dict) -> None:
    """Type-specific field population on the indicator."""
    fields = indicator_obj["fields"]
    if classification := item.get("classification"):
        fields["darkmonclassification"] = classification
    if sources := item.get("compromise_sources"):
        fields["darkmoncompromisesources"] = sources
    if first := item.get("first_compromise"):
        fields["darkmonfirstcompromise"] = first
    if last := item.get("last_compromise"):
        fields["darkmonlastcompromise"] = last
    if stealers := item.get("stealers"):
        fields["darkmonstealers"] = stealers


def test_module(client: Client) -> str:
    client.validate_api_key()
    return "ok"


def fetch_indicators_command(client: Client, params: dict) -> list[dict]:
    """TIM feed pull - returns indicator dicts XSOAR will ingest."""
    tlp_color = params.get("tlp_color")
    feed_tags = argToList(params.get("feedTags", ""))
    limit = arg_to_number(params.get("limit", DEFAULT_SIZE)) or DEFAULT_SIZE

    result = client.get_indicators(size=limit)
    ioc_objects = result.get("iocObjects", []) or []

    indicators: list[dict[str, Any]] = []
    for item in ioc_objects:
        ioc_type = item.get("type") or ""
        value = item.get("value")
        if not value:
            continue

        indicator_type = FEED_INDICATOR_TYPE_MAP.get(ioc_type, ioc_type)

        raw_data = {"value": value, "type": indicator_type, **item}
        indicator_obj: dict[str, Any] = {
            "value": value,
            "type": indicator_type,
            "service": VENDOR,
            "rawJSON": raw_data,
            "fields": {},
        }

        if event_info := item.get("eventInfo"):
            indicator_obj["fields"]["description"] = event_info
        if ts := item.get("timestamp"):
            indicator_obj["fields"]["firstseenbysource"] = ts
            indicator_obj["fields"]["lastseenbysource"] = ts

        _apply_indicator_fields(item, ioc_type, indicator_obj)

        if feed_tags:
            existing = indicator_obj["fields"].get("tags", [])
            indicator_obj["fields"]["tags"] = existing + feed_tags

        if tlp_color:
            indicator_obj["fields"]["trafficlightprotocol"] = tlp_color

        indicators.append(indicator_obj)

    return indicators


def darkmon_get_indicators_command(client: Client, args: dict) -> CommandResults:
    """Debug: fetch a page of indicators on demand."""
    limit = arg_to_number(args.get("limit", DEFAULT_SIZE)) or DEFAULT_SIZE
    raw = client.get_indicators(size=limit)
    iocs = raw.get("iocObjects", []) or []
    table = tableToMarkdown(
        f"{VENDOR} Indicators (page)",
        iocs,
        headers=["type", "value", "classification", "timestamp"],
        removeNull=True,
    )
    return CommandResults(
        outputs_prefix="Darkmon.Indicator",
        outputs_key_field="value",
        outputs=iocs,
        raw_response=raw,
        readable_output=table,
    )


def main() -> None:
    params = demisto.params()
    args = demisto.args()
    command = demisto.command()

    api_key = (params.get("X-API-KEY") or {}).get("password")
    if not api_key:
        raise DemistoException("API key is required")

    base_url = (params.get("base_url") or DEFAULT_BASE_URL).rstrip("/")
    headers = {"X-API-KEY": api_key, "Accept": "application/json"}

    client = Client(
        base_url=base_url,
        headers=headers,
        verify=not params.get("insecure", False),
        proxy=params.get("proxy", False),
    )

    try:
        if command == "test-module":
            return_results(test_module(client))
        elif command == "fetch-indicators":
            indicators = fetch_indicators_command(client, params)
            for batch_indicators in batch(indicators, batch_size=2000):
                demisto.createIndicators(batch_indicators)
        elif command == "darkmon-get-indicators":
            return_results(darkmon_get_indicators_command(client, args))
        else:
            raise NotImplementedError(f"Command {command} is not implemented")
    except Exception as e:
        return_error(f"Failed to execute {command} command. Error: {str(e)}")


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