Maltiverse

Use the Maltiverse integration to analyze suspicious hashes, URLs, domains and IP addresses.

Data Enrichment & Threat Intelligence · Maltiverse

Details

IDMaltiverse
ProviderMaltiverse
CategoryData Enrichment & Threat Intelligence
From Version5.0.0
Docker Imagedemisto/python3:3.12.13.10116658
Supported ModulesAgentix XSIAM

README

Analyze suspicious hashes, URLs, domains and IP addresses
This integration was integrated and tested with version 1.1 of Maltiverse

Use Cases


  1. Enriching information about different IOC types.
  2. Search for Reputation about different IOC types.
  3. Calculate DBot Score for indicators.

Configure Maltiverse on Cortex XSOAR


  1. Navigate to Settings > Integrations > Servers & Services.
  2. Search for Maltiverse.
  3. Click Add instance to create and configure a new integration instance.
    • Name: a textual name for the integration instance.
    • API Key
    • Source Reliability: Reliability of the source providing the intelligence data. The default value is C - Fairly reliable.
    • Trust any certificate (not secure)
    • Use system proxy settings
  4. Click Test to validate the URLs, token, and connection.

Commands


You can execute these commands from the Cortex XSOAR 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.

  1. ip
  2. domain
  3. url
  4. file

1. ip


Checks the reputation of an IP address

Base Command

ip

Input
Argument Name Description Required
ip IP address to check Required
threshold If the number of positives is higher than the threshold, the IP address will be considered malicious. If the threshold is not specified, the default IP threshold, as configured in the instance settings, will be used. Optional
fullResponse Return all of the results, note that it can be thousands of results. Prefer not to use in playbooks. The default value is “false Optional
Context Output
Path Type Description
IP.Address String The checked IP address
IP.Geo.Country String The country code of the IP address
IP.Malicious.Description Unknown Describes the reason for the IP to be in the block list
IP.PositiveDetections Number The number of sources that positively reported the indicator as block list
IP.Tags String The type of indicator
IP.ThreatTypes Unknown A list with the description of the elements in the block list
DBotScore.Score Number The DBot score
DBotScore.Type String The type of indicator
DBotScore.Vendor String The vendor used to calculate the score
DBotScore.Indicator String The indicator that was tested
Maltiverse.IP.Address String The checked IP address
Maltiverse.IP.Classification Unknown Qualitative maliciousness classification for an IoC. Possible values are malicious, suspicious, neutral and whitelisted
Maltiverse.IP.Blacklist.FirstSeen Date First time that the IoC has been seen
Maltiverse.IP.Blacklist.LastSeen Date Last time that the IoC has been seen
Maltiverse.IP.Blacklist.Description String Describes the reason for the IP to be in the block list
Maltiverse.IP.Blacklist.Source String The name of sources that reported the indicator
Maltiverse.IP.Tags String The type of indicator
Command Example

!ip ip=8.8.8.8

Human Readable Output

image

2. domain


Checks the reputation of a Domain

Notice: Submitting indicators using this command might make the indicator data publicly available. See the vendor’s documentation for more details.

Base Command

domain

Input
Argument Name Description Required
domain Domain address to check Required
threshold If the number of positives is higher than the threshold, the domain will be considered malicious. If the thershold is not specified, the default domain threshold, as configured in the instance settings, will be used. Optional
fullResponse Return all of the results, note that it can be thousands of results. Prefer not to use in playbooks. The default value is “false Optional
Context Output
Path Type Description
Domain.Name String The domain name
Domain.CreationDate Date Date when a IoC has been inserted for the first time
Domain.ModificationDate Date Date when a IoC has been updated for the last time
Domain.TLD Number Top Level Domain of the hostname
Domain.ASName String Autonumous system name of the domain
Domain.Tags String Attribute to label an IoC
Domain.ThreatTypes Unknown A list with the description of the elements in the block list
DBotScore.Score Number The DBot score
DBotScore.Type String The type of indicator
DBotScore.Vendor String The vendor used to calculate the score
DBotScore.Indicator String The indicator that was tested
Maltiverse.Domain.Address String The domain name
Maltiverse.Domain.Classification String Qualitative maliciousness classification for an IoC. Possible value are malicious, suspicious, neutral and whitlist
Maltiverse.Domain.Blacklist.Firstseen Date First time that the IoC was seen
Maltiverse.Domain.Blacklist.LastSeen Date Last time that the IoC was seen
Maltiverse.Domain.Blacklist.Description Unknown Describes the reason for the domain to be in the block list
Maltiverse.Domain.Blacklist.Source String The name of sources that reported the indicator
Maltiverse.Domain.Tags String Attribute to label an IoC
Maltiverse.Domain.ModificationTime Date Date when the IoC was updated for the last time
Maltiverse.Domain.CreationTime Date Date when a IoC was inserted for the first time
Maltiverse.Domain.TLD String Top level domain of the hostname
Command Example

!domain domain=google.com

Human Readable Output

image

3. url


Checks the reputation of an URL

Notice: Submitting indicators using this command might make the indicator data publicly available. See the vendor’s documentation for more details.

Base Command

url

Input
Argument Name Description Required
url URL address to check Required
threshold If the number of positives is higher than the threshold, the URL address will be considered malicious. If the threshold is not specified, the default URL threshold, as configured in the instance settings, will be used. Optional
fullResponse Return all of the results, note that it can be thousands of results. Prefer not to use in playbooks. The default value is “false” Optional
Context Output
Path Type Description
URL.Data String The URL
URL.Malicious.Description String Describes the reason for the URL to be in the block list
URL.Malicious.Vendor String The vendor that sends the indicator for reputation check.
URL.PositiveDetections Number The number of sources that positively reported the indicator as block list
URL.Tags String Attribute to label an IoC
URL.ThreatTypes Unknown A list with the description of the elements in the block list
DBotScore.Score Number The DBot score
DBotScore.Type String The type of indicato
DBotScore.Vendor String The vendor used to calculate the score
DBotScore.Indicator String The indicator that was tested
Maltiverse.URL.Address String The checked URL
Maltiverse.URL.Classification String Qualitative maliciousness classification for an IoC. Possible values are malicious, suspicious, neutral and whitelist
Maltiverse.URL.Blacklist.FirstSeen Date First time that the IoC has been seen
Maltiverse.URL.Blacklist.LastSeen Date Last time that the IoC was seen
Maltiverse.URL.Blacklist.Description Date Describes the reason for the URL to be in the block list
Maltiverse.URL.Blacklist.Source String The name of sources that reported the indicator.
Maltiverse.URL.Tags String Attribute to label an IoC
Maltiverse.URL.ModificationTime Date Date when the IOC has been updated for the last time.
Maltiverse.URL.CreationTime Date Date when a IOC has been inserted for the first time
Maltiverse.URL.Hostname String Stores the hostname to which the url belongs
Maltiverse.URL.Domain String Stores the domain to which the hostname belongs. Hostame and domain can match on level 2 hostnames
Maltiverse.URL.TLD String Top level domain of the hostname
Command Example

!url url=https://dv-expert.org

Human Readable Output

image

4. file


Check the reputation of a file

Base Command

file

Input
Argument Name Description Required
file SHA256 to check Required
threshold If the number of positives AV detection is higher than the threshold, the file will be considered malicious. If the threshold is not specified, the default file threshold, as configured in the instance settings, will be used. Optional
fullResponse Return all of the results, note that it can be thousands of results. Prefer not to use in playbooks. The default value is “false” Optional
Context Output
Path Type Description
File.Name String The full file name (including file extension).
File.MD5 String File MD5 hash
File.SHA1 String The SHA1 hash of the file
File.SHA256 String The SHA256 hash of the file
File.Size Number The size of the file in bytes
File.Extension String The extension of the file
File.Type String Description of the file type based on its magic numbers
File.Path String The path of the file
File.Tags String Attribute to label an IoC
File.ThreatTypes Unknown A list with the description of the elements in the block list
DBotScore.Score Number The DBot score
DBotScore.Type String The type of indicator
DBotScore.Vendor String The vendor used to calculate the score
DBotScore.Indicator String The indicator that was tested
Maltiverse.File.Score Number Qualitative scoring of the maliciousness of the file. Values from 0 to 100.
Maltiverse.File.Tags String Attribute to label an IOC
Maltiverse.File.Malicious.Vendor String For malicious files, the vendor that made the decision
Maltiverse.File.Malicious.Description String For malicious files, describes the reason for the file to be malicious
Maltiverse.File.PositiveDetections Number The number of sources that positively reported the indicator as block list.
Maltiverse.File.Name String The file name
Maltiverse.File.Classification String Qualitative maliciousness classification for an IoC. Possible values are malicious, suspicious, neutral and whitelist
Maltiverse.File.Blacklist.FirstSeen Date First time that the IoC has been seen.
Maltiverse.File.Blacklist.LastSeen Date Last time that the IoC was seen
Maltiverse.File.Blacklist.Description String Describes the reason for the URL to be in the block list
Maltiverse.File.Blacklist.Source String The name of sources that reported the indicator
Maltiverse.File.ModificationTime Date Date when the IOC has been updated for the last time
Maltiverse.File.CreationTime Date Date when a IOC has been inserted for the first time
Maltiverse.File.Size Number Size of the file in bytes
Maltiverse.File.ProcessList String List of processes raised by the file in runtime.
Maltiverse.File.ContactedHost String List of the IP addresses contacted by the sample in runtime
Maltiverse.File.DNSRequest String List of hostnames resolved by the sample in runtime
Command Example

!file file=edb2f88c29844117cd74acf8bb357edf92487a1b142fe6f60b6ac5e15d2d718f

Human Readable Output

image

Additional Information


For additional information please visit: https://whatis.maltiverse.com/

Known Limitations


  • When not creating an Maltiverse account there is a limit of 20 API calls per day.
    A free account grants 100 API calls per day.
    Please see https://maltiverse.com/plans for more information about the different plans.
  • URL command: When running the !url command, an URL may be followed by a ‘/’ at the end. Maltiverse requires this
    ‘/’ but it might cause the indicator to not show in the war room.

Configuration parameters

  • server_url — Server URL
  • api_key — API Key
  • credentials_api_key
  • integrationReliability — Source Reliability (required)
  • proxy — Use system proxy settings
  • insecure — Trust any certificate (not secure)

Commands (4)

  • domain

    Checks the reputation of a domain.

  • file

    Checks the reputation of a file by file hash.

  • ip

    Checks the reputation of an IP address.

  • url

    Checks the reputation of an URL.

import demistomock as demisto
from CommonServerPython import *
from CommonServerUserPython import *

""" IMPORTS """
from typing import Any
from _collections import defaultdict
import urllib3
import hashlib

# Disable insecure warnings
urllib3.disable_warnings()

""" CONSTANTS """
SERVER_URL = "https://api.maltiverse.com"
DBOT_SCORE_KEY = "DBotScore(val.Indicator == obj.Indicator && val.Vendor == obj.Vendor)"
DEFAULT_THRESHOLD = 5


class Error(Exception):
    """Base class for exceptions in this module."""


class NotFoundError(Error):
    """Exception raised for 404 - Page Not Found errors.

    Attributes:
        message -- explanation of the error
    """

    def __init__(self, message):
        self.message = message


class Client(BaseClient):
    """
    Client will implement the service API, and should not contain any Demisto logic.
    Should only do requests and return data.
    """

    def __init__(self, url: str, use_ssl: bool, use_proxy: bool, auth_token=None, reliability=DBotScoreReliability.C):
        self.auth_token = auth_token
        self.reliability = reliability
        super().__init__(url, verify=use_ssl, proxy=use_proxy, headers={"Accept": "application/json"})
        if self.auth_token:
            self._headers.update({"Authorization": "Bearer " + self.auth_token})

    def http_request(self, method, url_suffix):
        ok_codes = (200, 401, 403, 404, 500)  # includes responses that are ok (200) and error responses that should be
        # handled by the client and not in the BaseClient
        try:
            res = self._http_request(method, url_suffix, resp_type="response", ok_codes=ok_codes)
            if res.status_code == 200:
                try:
                    return res.json()
                except ValueError as exception:
                    raise DemistoException(f"Failed to parse json object from response: {res.content}", exception)

            if res.status_code in [401, 403, 500]:
                try:
                    err_msg = str(res.json())
                    if self.auth_token:
                        err_msg = f"Check server URL and API key \n{err_msg}"
                    else:
                        err_msg = f"Check server URL or try using an API key \n{err_msg}"
                except ValueError:
                    err_msg = "Check server URL or API key -\n" + str(res)
                raise DemistoException(err_msg)

            if res.status_code == 404:
                raise NotFoundError("Page Not Found")

        except Exception as e:
            if "<requests.exceptions.ConnectionError>" in e.args[0]:
                raise DemistoException(
                    "Connection error - Verify that the server URL parameter is correct and that "
                    "you have access to the server from your host.\n"
                )
            raise e

    def ip_report(self, ip: str) -> dict:
        if not is_ip_valid(ip):
            raise DemistoException("The given IP was invalid")
        return self.http_request("GET", f"/ip/{ip}")

    def url_report(self, url: str) -> dict:
        sha256_url = urlToSHA256(url)
        try:
            report = self.http_request("GET", f"/url/{sha256_url}")
            return report
        except NotFoundError:
            LOG(f"URL {url} was not found")
            return {"NotFound": True}
        except Exception as e:
            raise e

    def domain_report(self, domain: str) -> dict:
        return self.http_request("GET", f"/hostname/{domain}")

    def file_report(self, sha256: str) -> dict:
        try:
            report = self.http_request("GET", f"/sample/{sha256}")
            return report
        except NotFoundError:
            LOG(f"file {sha256} was not found")
            return {"NotFound": True}
        except Exception as e:
            raise e


def test_module(client=None):
    """
    Returning 'ok' indicates that the integration works like it is supposed to. Connection to the service is successful.

    Args:
        client: Maltiverse client

    Returns:
        'ok' if test passed, anything else will fail the test.
    """
    try:
        client.http_request("GET", "/ip/8.8.8.8")
        return "ok"
    except NotFoundError as e:
        return_error("Check server URL - " + e.message)
    except Exception as e:
        raise e


def calculate_score(positive_detections: int, classification: str, threshold: int, anti_virus: int = 0) -> int:
    """
    Calculates Demisto score based on the classification of Maltiverse and number of positive detections in the blacklist.

    Args:
        positive_detections (int): the number of items in the blacklist
        classification (str): the classification given to the IoC by Maltiverse. Can be one of: neutral, whitelist,
        suspicious, malicious
        threshold (int): the score threshold configured by the user.
        anti_virus (int) - optional: used to calculate the score only in the case that the IoC is a file. Indicates the
        number of items in the list of antivirus detections.

    Returns:
        int - Demisto's score for the indicator
    """
    if positive_detections == 0 and classification == "neutral":
        return 0
    elif classification == "whitelist":
        return 1
    elif positive_detections <= threshold and classification != "malicious":
        if anti_virus > 1:
            return 3
        return 2
    elif positive_detections > threshold or classification == "malicious":
        return 3
    else:  # if reached this line there is a problem with the logic
        return -1


def urlToSHA256(url: str) -> str:
    """
    Converts a url into its SHA256 hash.

    Args:
        url (str): the url that should be converted into  SHA256

    Returns:
        str - the SHA256 hash of the url
    """
    return hashlib.sha256(url.encode("utf-8")).hexdigest()


def create_blacklist_keys(blacklist):
    """
    Converts the Blacklist keys into context keys format.

    Args:
    blacklist (list): a list of dictionaries, where each dictionary is a positive detection of the IoC

    Returns:
    A new blacklist in which the keys are context keys
    """
    if not blacklist:
        return []
    new_blacklist = []
    for detection in blacklist:
        detection_keys = {}
        for key in detection:
            detection_keys[string_to_context_key(key)] = detection[key]
        new_blacklist.append(detection_keys)
    return new_blacklist


def create_tags(tags: list) -> list:
    """
    Removes all urls from the tags list

    Args:
    tags (list): a list of tags as returned by Maltiverse

    Returns:
    A new list that includes all the tags given by Maltiverse without urls
    """
    clean_tags = []
    if tags:
        for tag in tags:
            if not re.match(urlRegex, tag):
                clean_tags.append(tag)
    return clean_tags


def ip_command(client: Client, args: dict[str, str]) -> tuple[str, dict, Any]:
    """
    Executes IP enrichment against Maltiverse.

    Args:
        client (Client): Maltiverse client.
        args (Dict[str, str]): the arguments for the command.
    Returns:
        str: human readable presentation of the IP report.
        dict: the results to return into Demisto's context.
        Any: the raw data from Maltiverse client (used for debugging).
    """

    threshold = int(args.get("threshold", DEFAULT_THRESHOLD))
    markdown = ""
    context: dict = defaultdict(list)
    reports = []

    for ip in argToList(args.get("ip")):
        report = client.ip_report(ip)
        positive_detections = len(report.get("blacklist", []))

        blacklist_context = {"Blacklist": report.get("blacklist", [])}
        blacklist_context["Blacklist"] = create_blacklist_keys(blacklist_context["Blacklist"])
        blacklist_description = [
            blacklist_context["Blacklist"][i]["Description"] for i in range(len(report.get("blacklist", [])))
        ]

        outputs = {
            "Address": report.get("ip_addr", ""),
            "Geo": {"Country": report.get("country_code", "")},
            "PositiveDetections": positive_detections,
            "Malicious": {"Description": blacklist_description},
            "Tags": create_tags(report.get("tag", "")),
            "ThreatTypes": {"threatcategory": blacklist_description},
        }

        additional_info = {
            "Tags": create_tags(report.get("tag", "")),
            "Classification": report.get("classification", ""),
            "Address": report.get("ip_addr", ""),
        }

        dbot_score = {
            "Indicator": report.get("ip_addr", ""),
            "Type": "ip",
            "Vendor": "Maltiverse",
            "Score": calculate_score(positive_detections, report.get("classification", ""), threshold),
            "Reliability": client.reliability,
        }

        maltiverse_ip = {**blacklist_context, **additional_info}

        context[outputPaths["ip"]].append(outputs)
        context[f'Maltiverse.{outputPaths["ip"]}'].append(maltiverse_ip)
        context[DBOT_SCORE_KEY].append(dbot_score)

        markdown = f'## Maltiverse IP reputation for: {report["ip_addr"]}\n'
        markdown += f'IP Address: **{report.get("ip_addr", "")}**\n'
        markdown += f'Country: **{report.get("country_code", "")}**\n'
        markdown += f"Positive Detections: **{positive_detections}**\n"
        markdown += f'Maltiverse Classification: **{report.get("classification", "")}**\n'
        if positive_detections:
            markdown += tableToMarkdown(
                "Blacklist",
                blacklist_context["Blacklist"],
                removeNull=True,
                headers=["Source", "Description", "FirstSeen", "LastSeen"],
            )

        reports.append(report)

    return markdown, context, reports


def url_command(client: Client, args: dict[str, str]) -> tuple[str, dict, Any]:
    """
    Executes URL enrichment against Maltiverse.

    Args:
        client (Client): Maltiverse client.
        args (Dict[str, str]): the arguments for the command.
    Returns:
        str: human readable presentation of the URL report.
        dict: the results to return into Demisto's context.
        Any: the raw data from Maltiverse client (used for debugging).
    """

    threshold = int(args.get("threshold", DEFAULT_THRESHOLD))
    markdown = ""
    context: dict = defaultdict(list)
    reports = []

    for url in argToList(args.get("url", "")):
        report = client.url_report(url)
        if "NotFound" in report:
            markdown += f"No results found for {url}"
            dbot_score = {"Indicator": url, "Type": "Url", "Vendor": "Maltiverse", "Score": 0, "Reliability": client.reliability}
            context[DBOT_SCORE_KEY].append(dbot_score)
            break
        positive_detections = len(report.get("blacklist", []))
        blacklist_context = {"Blacklist": report.get("blacklist", [])}
        blacklist_context["Blacklist"] = create_blacklist_keys(blacklist_context["Blacklist"])

        outputs = {
            "Data": report.get("url", ""),
            "PositiveDetections": positive_detections,
            "Tags": create_tags(report.get("tag", "")),
            "ThreatTypes": {
                "threatcategory": [
                    blacklist_context["Blacklist"][i]["Description"] for i in range(len(report.get("blacklist", [])))
                ]
            },
        }

        dbot_score = {
            "Indicator": url,
            "Type": "Url",
            "Vendor": "Maltiverse",
            "Score": calculate_score(positive_detections, report.get("classification", ""), threshold),
            "Reliability": client.reliability,
        }

        maltiverse_url = {
            string_to_context_key(field): report.get(field, "")
            for field in ["classification", "modification_time", "creation_time", "hostname", "domain", "tld"]
        }
        maltiverse_url["Address"] = report.get("url", "")
        maltiverse_url["Tags"] = create_tags(report.get("tag", ""))
        maltiverse_url = {**maltiverse_url, **blacklist_context}

        markdown = f"## Maltiverse URL reputation for: {url}\n"
        markdown += f"URL: {url}\n"
        markdown += f'URL Domain: **{report.get("domain", "")}**\n'
        markdown += f'URL Creation Time: **{report.get("creation_time", "")}**\n'
        markdown += f'URL Modification Time: **{report.get("modification_time", "")}**\n'
        markdown += f"Positive Detections: **{positive_detections}**\n"
        markdown += f'Maltiverse Classification: **{report.get("classification", "")}**\n'

        if positive_detections:
            malicious_info = {
                "Malicious": {
                    "Description": [
                        blacklist_context["Blacklist"][i]["Description"] for i in range(len(report.get("blacklist", [])))
                    ],
                    "Vendor": "Maltiverse",
                }
            }
            outputs = {**outputs, **malicious_info}
            markdown += "URL Malicious Vendor: **Maltiverse**\n"
            markdown += tableToMarkdown(
                "Blacklist",
                blacklist_context["Blacklist"],
                removeNull=True,
                headers=["Source", "Description", "FirstSeen", "LastSeen"],
            )

        context[outputPaths["url"]].append(outputs)
        context[DBOT_SCORE_KEY].append(dbot_score)
        context[f'Maltiverse.{outputPaths["url"]}'].append(maltiverse_url)

        reports.append(report)

    return markdown, context, reports


def domain_command(client: Client, args: dict[str, str]) -> tuple[str, dict, Any]:
    """
    Executes domain enrichment against Maltiverse.

    Args:
        client (Client): Maltiverse client.
        args (Dict[str, str]): the arguments for the command.
    Returns:
        str: human readable presentation of the domain report.
        dict: the results to return into Demisto's context.
        Any: the raw data from Maltiverse client (used for debugging).
    """
    threshold = int(args.get("threshold", DEFAULT_THRESHOLD))
    markdown = ""
    context: dict = defaultdict(list)
    reports = []

    for domain in argToList(args.get("domain", "")):
        report = client.domain_report(domain)
        positive_detections = len(report.get("blacklist", []))

        blacklist_context = {"Blacklist": report.get("blacklist", [])}
        blacklist_context["Blacklist"] = create_blacklist_keys(blacklist_context["Blacklist"])

        outputs = {string_to_context_key(field): report.get(field, "") for field in ["creation_time", "modification_time"]}
        outputs["Tags"] = create_tags(report.get("tag", ""))
        outputs["TLD"] = report.get("tld", "")
        outputs["Name"] = report.get("hostname", "")
        outputs["ASName"] = report.get("as_name", "")
        outputs["ThreatTypes"] = {
            "threatcategory": [blacklist_context["Blacklist"][i]["Description"] for i in range(len(report.get("blacklist", [])))]
        }

        dbot_score = {
            "Indicator": domain,
            "Type": "Domain",
            "Vendor": "Maltiverse",
            "Score": calculate_score(positive_detections, report.get("classification", ""), threshold),
            "Reliability": client.reliability,
        }

        maltiverse_domain = {
            string_to_context_key(field): report.get(field, "")
            for field in ["creation_time", "modification_time", "classification"]
        }
        maltiverse_domain["TLD"] = report.get("tld", "")
        maltiverse_domain["Tags"] = create_tags(report.get("tag", ""))
        maltiverse_domain["Address"] = report.get("hostname", "")
        maltiverse_domain = {**maltiverse_domain, **blacklist_context}

        context[outputPaths["domain"]].append(outputs)
        context[DBOT_SCORE_KEY].append(dbot_score)
        context[f'Maltiverse.{outputPaths["domain"]}'].append(maltiverse_domain)

        markdown = f'## Maltiverse Domain reputation for: {report.get("hostname", "")}\n'
        markdown += f'Domain Name: {report.get("hostname", "")}\n'
        markdown += f'Domain Creation Time: **{report.get("creation_time", "")}**\n'
        markdown += f'Domain Modification Time: **{report.get("modification_time", "")}**\n'
        markdown += f'Maltiverse Classification: **{report.get("classification", "")}**\n'

        reports.append(report)

    return markdown, context, reports


def file_command(client: Client, args: dict[str, str]) -> tuple[str, dict, Any]:
    """
    Executes file hash enrichment against Maltiverse.

    Args:
        client (Client): Maltiverse client.
        args (Dict[str, str]): the arguments for the command.

    Returns:
         str: human readable presentation of the file hash report.
         dict: the results to return into Demisto's context.
         Any: the raw data from Maltiverse client (used for debugging).
    """
    threshold = int(args.get("threshold", DEFAULT_THRESHOLD))
    markdown = ""
    context: dict = defaultdict(list)
    reports = []

    for file in argToList(args.get("file", "")):
        report = client.file_report(file)
        if "NotFound" in report:
            markdown += f"No results found for file hash {file}"
            dbot_score = {
                "Indicator": file,
                "Type": "File",
                "Vendor": "Maltiverse",
                "Score": 0,
                "Reliability": client.reliability,
            }
            context[DBOT_SCORE_KEY].append(dbot_score)
            break
        positive_detections = len(report.get("blacklist", []))

        blacklist_context = {"Blacklist": report.get("blacklist", [])}
        blacklist_context["Blacklist"] = create_blacklist_keys(blacklist_context["Blacklist"])

        outputs = {
            "Name": report["filename"][0],
            "MD5": report.get("md5", ""),
            "SHA1": report.get("sha1", ""),
            "SHA256": report.get("sha256", ""),
            "Size": report.get("size", ""),
            "Type": report.get("type", ""),
            "Extension": (report["filename"][0]).split(".")[-1],
            "Path": report.get("process_list", [{}])[0].get("normalizedpath"),
            "Tags": create_tags(report.get("tag", "")),
            "ThreatTypes": {
                "threatcategory": [
                    blacklist_context["Blacklist"][i]["Description"] for i in range(len(report.get("blacklist", [])))
                ]
            },
        }

        dbot_score = {
            "Indicator": file,
            "Type": "File",
            "Vendor": "Maltiverse",
            "Score": calculate_score(
                positive_detections, report.get("classification", ""), threshold, len(report.get("antivirus", []))
            ),
            "Reliability": client.reliability,
        }

        process_list = {
            "ProcessList": {
                string_to_context_key(field): report.get("process_list", [{}])[0].get(field)
                for field in ["name", "normalizedpath", "sha256", "uid"]
            }
        }
        file_malicious = {
            "Malicious": {
                "Vendor": "Maltiverse",
                "Description": [
                    blacklist_context["Blacklist"][i]["Description"] for i in range(len(report.get("blacklist", [])))
                ],
            }
        }

        maltiverse_file = {
            string_to_context_key(field): report.get(field, "")
            for field in [
                "score",
                "classification",
                "modification_time",
                "creation_time",
                "size",
                "contacted_host",
                "dns_request",
            ]
        }
        maltiverse_file["PositiveDetections"] = positive_detections
        maltiverse_file["Name"] = report["filename"][0]
        maltiverse_file["Tags"] = create_tags(report.get("tag", ""))

        maltiverse_file = {**maltiverse_file, **process_list}
        maltiverse_file = {**maltiverse_file, **blacklist_context}
        if positive_detections > 0:
            maltiverse_file = {**maltiverse_file, **file_malicious}

        context[outputPaths["file"]].append(outputs)
        context[DBOT_SCORE_KEY].append(dbot_score)
        context[f'Maltiverse.{outputPaths["file"]}'].append(maltiverse_file)

        markdown = f'## Maltiverse File reputation for: {report["filename"][0]}\n'
        markdown += f'File Name: {report["filename"][0]}\n'
        markdown += f'File SHA256: **{report.get("sha256", "")}**\n'
        markdown += f'File Type: **{report.get("type", "")}**\n'
        markdown += f"Positive Detections: **{positive_detections}**\n"
        markdown += f'Maltiverse Classification: **{report.get("classification", "")}**\n'

        reports.append(report)

    return markdown, context, reports


def main():
    params = demisto.params()
    server_url = params.get("server_url") if params.get("server_url") else SERVER_URL
    reliability = params.get("integrationReliability", "C - Fairly reliable")

    if DBotScoreReliability.is_valid_type(reliability):
        reliability = DBotScoreReliability.get_dbot_score_reliability_from_str(reliability)
    else:
        return_error("Please provide a valid value for the Source Reliability parameter.")

    client = Client(
        url=server_url,
        use_ssl=not params.get("insecure", False),
        use_proxy=params.get("proxy", False),
        auth_token=params.get("credentials_api_key", {}).get("password") or params.get("api_key", None),
        reliability=reliability,
    )

    commands = {
        "ip": ip_command,
        "url": url_command,
        "domain": domain_command,
        "file": file_command,
    }

    command = demisto.command()
    LOG(f"Command being called is {command}")

    try:
        if command == "test-module":
            demisto.results(test_module(client))
        elif command in commands:
            return_outputs(*commands[command](client, demisto.args()))
        else:
            return_error("Command not found.")
    except Exception as e:
        return_error(f"Failed to execute {command} command. Error: {e}")


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