FireEye Detection on Demand

FireEye Detection On Demand is a threat detection service delivered as an API for integration into the SOC workflow, SIEM analytics, data repositories, or web applications, etc. It delivers flexible file and content analysis to identify malicious behavior wherever the enterprise needs it.

Forensics & Malware Analysis · FireEye Detection on Demand

Details

IDFireEye Detection on Demand
ProviderTrellix
CategoryForensics & Malware Analysis
From Version6.0.0
Docker Imagedemisto/python3:3.12.8.3296088
Supported ModulesAgentix XSIAM

README

FireEye Detection On Demand is a threat detection service delivered as an API for integration into the SOC workflow, SIEM analytics, data repositories, or web applications, etc. It delivers flexible file and content analysis to identify malicious behavior wherever the enterprise needs it.
This integration was integrated and tested with version 1.4.1 of FireEye Detection on Demand

Configure FireEye Detection on Demand in Cortex

Parameter Description Required
url DoD hostname True
apikey API Key True
insecure Trust any certificate (not secure) False
proxy 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.

fireeye-dod-get-hashes


Queries FireEye Detection on Demand reports for the provided md5 hashes

Base Command

fireeye-dod-get-hashes

Input

Argument Name Description Required
md5_hashes One or more comma separated MD5 hashes to get the reputation of. Required

Context Output

Path Type Description
DBotScore.Indicator string The indicator that was tested.
DBotScore.Score number The actual score.
DBotScore.Type unknown The indicator type.
DBotScore.Vendor unknown The vendor used to calculate the score.
File.Malicious.Vendor unknown N/A
File.MD5 unknown The MD5 hash of the file
FireEyeDoD.engine_results.cache_lookup.sha256 String The sha256 value of the file
FireEyeDoD.engine_results.cache_lookup.signature_name String The name of the virus signature
FireEyeDoD.engine_results.cache_lookup.is_malicious Number True/False if the file is malicious
FireEyeDoD.engine_results.cache_lookup.verdict String The overall verdict of all analysis engines
FireEyeDoD.engine_results.cache_lookup.file_extension String The extension of the file
FireEyeDoD.engine_results.cache_lookup.weight Number How important this engine result is to determining malicious activity
FireEyeDoD.engine_results.dynamic_analysis.verdict String This particular engine’s verdict on whether or not the file is malicious
FireEyeDoD.engine_results.av_lookup.verdict String This particular engine’s verdict on whether or not the file is malicious
FireEyeDoD.engine_results.avs_lookup.verdict String This particular engine’s verdict on whether or not the file is malicious
FireEyeDoD.engine_results.dti_lookup.verdict String This particular engine’s verdict on whether or not the file is malicious
FireEyeDoD.md5 String The MD5 hash of the file
FireEyeDoD.is_malicious Number True/False if the file is malicious

Command Example

!fireeye-dod-get-hashes md5_hashes=47f9fdc617f8c98a6732be534d8dbe9c

Context Example

{
    "DBotScore": {
        "Indicator": "47f9fdc617f8c98a6732be534d8dbe9c",
        "Score": 0,
        "Type": "file",
        "Vendor": "FireEye DoD"
    },
    "File": {
        "FireEyeDoD": {
            "engine_results": {
                "av_lookup": {
                    "verdict": "not_found"
                },
                "avs_lookup": {
                    "verdict": "not_found"
                },
                "cache_lookup": {
                    "verdict": "not_found"
                },
                "dti_lookup": {
                    "verdict": "not_found"
                },
                "dynamic_analysis": {
                    "verdict": "not_found"
                }
            },
            "is_malicious": false
        },
        "MD5": "47f9fdc617f8c98a6732be534d8dbe9c"
    }
}

Human Readable Output

FireEye DoD Results

MD5 SHA256 Malicious
47f9fdc617f8c98a6732be534d8dbe9c    

fireeye-dod-submit-file


Submits file to FireEye Detection on Demand for analysis

Base Command

fireeye-dod-submit-file

Input

Argument Name Description Required
entryID The file entry ID to submit. Required
password Password to be used by the detection engine to decrypt a password protected file. Optional
param Command line parameter(s) to be used by detection engine when running the file. Mainly applicable to .exe files. For example, setting param to “start -h localhost -p 5555” will make the detection engine run a file named “malicious.exe” as “malicious.exe start -h localhost -p 5555”. Optional
screenshot Extract screenshot of screen activity during dynamic analysis if true, which later can be downloaded with artifacts api Optional
video Extract video activity during dynamic analysis if true, which later can be downloaded with artifacts api Optional
fileExtraction Extract dropped files from vm during dynamic analysis if true, which later can be downloaded with artifacts api Optional
memoryDump Extract memory dump files from vm during dynamic analysis if true, which later can be downloaded with artifacts api Optional
pcap Extract pcap files from vm during dynamic analysis if true, which later can be downloaded with artifacts api Optional

Context Output

Path Type Description
FireEyeDoD.Scan.report_id unknown The report ID can be used to query the status and results of the file submission
FireEyeDoD.Scan.status unknown The current status of the file submission
FireEyeDoD.Scan.filename unknown The name of the file that was submitted

Command Example

!fireeye-dod-submit-file entryID=37@760083ae-625e-4a6c-8e93-87ece7964dd0

Context Example

{
    "FireEyeDoD": {
        "Scan": {
            "filename": "test-infection.exe",
            "md5": "47f9fdc617f8c98a6732be534d8dbe9a",
            "overall_status": "RUNNING",
            "report_id": "c1d32790-5b08-45ab-a3be-3e61f8826e8b"
        }
    }
}

Human Readable Output

Started analysis of test-infection.exe with FireEye Detection on Demand. Results will be published to report id: c1d32790-5b08-45ab-a3be-3e61f8826e8b

fireeye-dod-submit-urls


Submits URLs to FireEye Detection on Demand for analysis

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

Base Command

fireeye-dod-submit-urls

Input

Argument Name Description Required
urls A comma separated list of URLs to scan. Maximum of 10 per request. Required

Context Output

Path Type Description
FireEyeDoD.Scan.report_id unknown The ID of the report
FireEyeDoD.Scan.status unknown The status of the file submission. Will be “DONE” when all engines are finished.

Command Example

!fireeye-dod-submit-urls urls="https://www.google.com"

Context Example

{
    "FireEyeDoD": {
        "Scan": {
            "md5": "NA",
            "overall_status": "RUNNING",
            "report_id": "55223a00-6741-41c4-80a9-28d3c133a5db"
        }
    }
}

Human Readable Output

Started analysis of [‘https://www.google.com’] with FireEye Detection on Demand. Results will be published to report id: 55223a00-6741-41c4-80a9-28d3c133a5db

fireeye-dod-get-reports


Retrieves one or more reports of file scans

Base Command

fireeye-dod-get-reports

Input

Argument Name Description Required
report_ids A comma separated list of one or more report IDs to fetch. Required
extended_report If True, additional information will be returned Optional
get_screenshot Whether or not to get screenshot artifacts from the report Optional
get_artifact Which report artifacts to retrieve (if any) Optional

Context Output

Path Type Description
FireEyeDoD.Scan.report_id String The ID of the report
FireEyeDoD.Scan.overall_status String The overall status of all of the engines
FireEyeDoD.Scan.is_malicious Number True/False if the file is malicious
FireEyeDoD.Scan.started_at Date The UTC time the scan was started
FireEyeDoD.Scan.completed_at Date The UTC time the scan was completed
FireEyeDoD.Scan.duration Number How long, in seconds, the scan took to complete.
FireEyeDoD.Scan.file_name String The name of the submitted file
FireEyeDoD.Scan.file_size Number The size of the file in bytes
FireEyeDoD.Scan.file_extension String The extension of the submitted file. If a URL was submitted, this will be empty.
FireEyeDoD.Scan.md5 String The MD5 hash of the submitted file
FireEyeDoD.Scan.sha256 String The sha256 hash of the submitted file
FireEyeDoD.Scan.signature_name String List of signatures extracted by all engines

Command Example

!fireeye-dod-get-reports report_ids=82e71bec-04c7-4f04-945b-4d344a758abe

Context Example

{
    "FireEyeDoD": {
        "Scan": {
            "completed_at": "2020-11-10 14:28:18",
            "duration": 0,
            "file_extension": "urlscan",
            "file_name": "test-infection.exe",
            "file_size": 28672,
            "is_malicious": true,
            "magic": null,
            "md5": "NA",
            "name": "test-infection.exe",
            "overall_status": "DONE",
            "report_id": "82e71bec-04c7-4f04-945b-4d344a758abe",
            "sha1": "NA",
            "sha256": "NA",
            "signature_name": [
                "Phish.LIVE.DTI.URL",
                "Malicious.LIVE.DTI.URL",
                "fe_ml_heuristic",
                "FireEye.Malware.exe",
                "FETestEvent"
            ],
            "size": 28672,
            "started_at": "2020-11-10 14:28:18",
            "type": "urlscan",
            "urls": [
                "http://fedeploycheck.fireeye.com/appliance-test/block.html",
                "http://165.227.14.8/?NDU2MDgz&yOyeu&YPocHQsbD=disagree&lAjd=callous&mvUq=disagree&eSCpt=disagree&mnnYBwlX=abettor&MZMJ=everyone&ipEMqw=professional&xRefGF=callous&tzsdfga4=dJORROwbnhRaGKA1hlIhYVV0W8a2ojkbXzhCf1JaG9RGIZ1hD-sGcELgL6G2xyPNRcw&cvggd54=wnfQMvXcJBXQFYbIKuXDSKxDKU7WFEaVw4-RhMG3YpjNfynz1-zURnL6tASVVFuRrbM&hMdqbI=electrical&qgZufk=disagree&egHdAM=abettor&BUfBH=professional&RGVeFwBNTM2MzY2",
                "http://br430.teste.website/~idbrok92/idb/UI/Login/",
                "http://www.dulys.co.zw/",
                "http://fedeploycheck.fireeye.com/appliance-test/test-infection.exe",
                "http://fedeploycheck.fireeye.com/appliance-test/test-infection.pdf",
                "http://fedeploycheck.fireeye.com/appliance-test/alert.html",
                "https://tinyurl.com/y2qezvol",
                "https://fedeploycheck.fireeye.com/appliance-test/alert.html"
            ],
            "verdict": "MALICIOUS"
        }
    }
}

Human Readable Output

Scan status

completed_at duration file_extension file_name file_size is_malicious magic md5 name overall_status report_id sha1 sha256 signature_name size started_at type urls verdict
2020-11-10 14:28:18 0 urlscan test-infection.exe 28672 true   NA test-infection.exe DONE 82e71bec-04c7-4f04-945b-4d344a758abe NA NA Phish.LIVE.DTI.URL,
Malicious.LIVE.DTI.URL,
fe_ml_heuristic,
FireEye.Malware.exe,
FETestEvent
28672 2020-11-10 14:28:18 urlscan http://fedeploycheck.fireeye.com/appliance-test/block.html,
http://165.227.14.8/?NDU2MDgz&yOyeu&YPocHQsbD=disagree&lAjd=callous&mvUq=disagree&eSCpt=disagree&mnnYBwlX=abettor&MZMJ=everyone&ipEMqw=professional&xRefGF=callous&tzsdfga4=dJORROwbnhRaGKA1hlIhYVV0W8a2ojkbXzhCf1JaG9RGIZ1hD-sGcELgL6G2xyPNRcw&cvggd54=wnfQMvXcJBXQFYbIKuXDSKxDKU7WFEaVw4-RhMG3YpjNfynz1-zURnL6tASVVFuRrbM&hMdqbI=electrical&qgZufk=disagree&egHdAM=abettor&BUfBH=professional&RGVeFwBNTM2MzY2,
http://br430.teste.website/~idbrok92/idb/UI/Login/,
http://www.dulys.co.zw/,
http://fedeploycheck.fireeye.com/appliance-test/test-infection.exe,
http://fedeploycheck.fireeye.com/appliance-test/test-infection.pdf,
http://fedeploycheck.fireeye.com/appliance-test/alert.html,
https://tinyurl.com/y2qezvol,
https://fedeploycheck.fireeye.com/appliance-test/alert.html
MALICIOUS

fireeye-dod-get-report-url


Generates a pre-signed URL for a report

Base Command

fireeye-dod-get-report-url

Input

Argument Name Description Required
report_id The ID of the report to fetch Required
expiration Expiration (in hours) for browser viewable report pre-signed URL link. Default value is 72 hours. Minimum is 1 hour, and maximum is 8760 hours (365 days). Optional

Context Output

There is no context output for this command.

Command Example

!fireeye-dod-get-report-url report_id=82e71bec-04c7-4f04-945b-4d344a758abe

Context Example

{}

Human Readable Output

Report 82e71bec-04c7-4f04-945b-4d344a758abe is available here

Configuration parameters

  • url — DoD hostname (required)
  • apikey — API Key (required)
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings

Commands (5)

  • fireeye-dod-get-hashes

    Queries FireEye Detection on Demand reports for the provided md5 hashes.

  • fireeye-dod-get-report-url

    Generates a pre-signed URL for a report.

  • fireeye-dod-get-reports

    Retrieves one or more reports of file scans.

  • fireeye-dod-submit-file

    Submits file to FireEye Detection on Demand for analysis.

  • fireeye-dod-submit-urls

    Submits URLs to FireEye Detection on Demand for analysis.

from typing import Any

import dateparser
import demistomock as demisto  # noqa: F401
import urllib3
from CommonServerPython import *  # noqa: F401

# Disable insecure warnings
urllib3.disable_warnings()


""" CONSTANTS """


DATE_FORMAT = "%Y-%m-%dT%H:%M:%SZ"

""" CLIENT CLASS """


class Client(BaseClient):
    """Client class to interact with the service API

    This Client implements API calls, and does not contain any Demisto logic.
    Should only do requests and return data.
    It inherits from BaseClient defined in CommonServer Python.
    Most calls use _http_request() that handles proxy, SSL verification, etc.
    For this HelloWorld implementation, no special attributes defined
    """

    def get_file_reputation(self, file: str) -> dict[str, Any]:
        return self._http_request(method="GET", url_suffix=f"/hashes/{file}")

    def get_health(self) -> dict[str, Any]:
        return self._http_request(method="GET", url_suffix="/health")

    def submit_file(self, files: dict[str, Any], data: dict[str, Any]) -> dict[str, Any]:
        return self._http_request(method="POST", url_suffix="/files", files=files, data=data)

    def submit_urls(self, data: dict[str, Any]) -> dict[str, Any]:
        return self._http_request(method="POST", url_suffix="/urls", files=data, data=None)

    def get_report_url(self, report_id: str, expiration: int) -> dict[str, Any]:
        return self._http_request(method="GET", url_suffix=f"/presigned-url/{report_id}", params={"expiry": expiration})

    def report_status(self, report_id: str, extended: str) -> dict[str, Any]:
        return self._http_request(method="GET", url_suffix=f"/reports/{report_id}", params={"extended": extended})

    def report_artifact(self, report_id: str, artifact_type: str) -> dict[str, Any]:
        return self._http_request(
            method="GET",
            url_suffix=f"/artifacts/{report_id}",
            params={
                "type": artifact_type,
            },
            resp_type="content",
        )


""" HELPER FUNCTIONS """


def convert_to_demisto_severity(severity: str) -> int:
    # In this case the mapping is straightforward, but more complex mappings
    # might be required in your integration, so a dedicated function is
    # recommended. This mapping should also be documented.
    return {
        "Low": 1,  # low severity
        "Medium": 2,  # medium severity
        "High": 3,  # high severity
        "Critical": 4,  # critical severity
    }[severity]


def arg_to_int(arg: Any, arg_name: str, required: bool = False) -> int | None:
    if arg is None:
        if required is True:
            raise ValueError(f'Missing "{arg_name}"')
        return None
    if isinstance(arg, str):
        if arg.isdigit():
            return int(arg)
        raise ValueError(f'Invalid number: "{arg_name}"="{arg}"')
    if isinstance(arg, int):
        return arg
    raise ValueError(f'Invalid number: "{arg_name}"')


def arg_to_timestamp(arg: Any, arg_name: str, required: bool = False) -> int | None:
    if arg is None:
        if required is True:
            raise ValueError(f'Missing "{arg_name}"')
        return None

    if isinstance(arg, str) and arg.isdigit():
        # timestamp is a str containing digits - we just convert it to int
        return int(arg)
    if isinstance(arg, str):
        # we use dateparser to handle strings either in ISO8601 format, or
        # relative time stamps.
        # For example: format 2019-10-23T00:00:00 or "3 days", etc
        date = dateparser.parse(arg, settings={"TIMEZONE": "UTC"})
        if date is None:
            # if d is None it means dateparser failed to parse it
            raise ValueError(f"Invalid date: {arg_name}")

        return int(date.timestamp())
    if isinstance(arg, int | float):
        # Convert to int if the input is a float
        return int(arg)
    raise ValueError(f'Invalid date: "{arg_name}"')


""" COMMAND FUNCTIONS """


def test_module(client: Client) -> str:
    # INTEGRATION DEVELOPER TIP
    # Client class should raise the exceptions, but if the test fails
    # the exception text is printed to the Cortex XSOAR UI.
    # If you have some specific errors you want to capture (i.e. auth failure)
    # you should catch the exception here and return a string with a more
    # readable output (for example return 'Authentication Error, API Key
    # invalid').
    # Cortex XSOAR will print everything you return different than 'ok' as
    # an error
    try:
        #
        client.get_health()
    except DemistoException as e:
        if "Forbidden" in str(e):
            return "Authorization Error: make sure API Key is correctly set"
        else:
            raise e
    return "ok"


def get_hashes_command(client: Client, args: dict[str, Any]) -> tuple[str, dict, Any]:
    hashes = argToList(args.get("md5_hashes"))
    if len(hashes) == 0:
        raise ValueError("hash(es) not specified")

    for hash in hashes:
        if md5Regex.match(hash):
            continue
        raise Exception("Invalid hash. Only MD5 is supported.")

    dbot_score_list: list[dict[str, Any]] = []
    file_standard_list: list[dict[str, Any]] = []
    file_data_list: list[dict[str, Any]] = []

    for hash in hashes:
        file_data = client.get_file_reputation(hash)
        file_data["MD5"] = file_data["md5"]
        del file_data["md5"]
        # demisto.results(file_data)
        engines = file_data.get("engine_results", {})
        for key in engines:
            if engines[key].get("sha256"):
                file_data["SHA256"] = engines[key].get("sha256")
                del engines[key]["sha256"]
        # If the outer `is_malicious` is set to True, assume the score should be bad
        # Otherwise, default to unknown unless at least one engine has returned a verdict besides `not_found`
        if file_data["is_malicious"]:
            score = 3  # bad
        else:
            score = 0  # unknown
            for key in engines:
                verdict = engines[key].get("verdict", "not_found")
                if verdict != "not_found" and verdict != "malicious":
                    score = 1  # good
                    break

        dbot_score = {"Indicator": hash, "Vendor": "FireEye DoD", "Type": "file", "Score": score}
        file_standard_context = {
            "MD5": hash,
        }

        if score == 3:
            # if score is bad must add DBotScore Vendor and Description
            file_standard_context["Malicious"] = {"Vendor": "FireEye DoD"}

        filedata = {}
        filedata["FireEyeDoD"] = file_data
        filedata["MD5"] = file_data["MD5"]
        del filedata["FireEyeDoD"]["MD5"]
        if file_data.get("SHA256"):
            dbot_score_sha256 = {"Indicator": file_data.get("SHA256"), "Vendor": "FireEye DoD", "Type": "file", "Score": score}
            dbot_score_list.append(dbot_score_sha256)
            filedata["SHA256"] = file_data["SHA256"]
            file_standard_context["SHA256"] = file_data["SHA256"]
            del filedata["FireEyeDoD"]["SHA256"]

        file_standard_list.append(file_standard_context)
        dbot_score_list.append(dbot_score)
        file_data_list.append(filedata)

    outputs = {
        "DBotScore(val.Vendor == obj.Vendor && val.Indicator == obj.Indicator)": dbot_score_list,
        outputPaths["file"]: file_standard_list,
        "File(val.MD5 == obj.MD5 || val.SHA256 == obj.SHA256)": file_data_list,
    }

    readable_output = tableToMarkdown("FireEye DoD Results", file_standard_list, headers=["MD5", "SHA256", "Malicious"])

    return (readable_output, outputs, file_data_list)


def generate_report_url(client: Client, args: dict[str, Any]) -> tuple[str, dict, dict]:
    report_id = str(args.get("report_id"))
    expiration = arg_to_int(arg=args.get("expiration"), arg_name="expiration", required=True)
    if expiration:
        if expiration < 1 or expiration > 8760:
            raise ValueError("Expiration must be between 1 and 8760 hours.")
    else:
        raise ValueError("Expiration not specified or not a number.")

    report = client.get_report_url(report_id=report_id, expiration=expiration)
    presigned_report_url = report.get("presigned_report_url")

    readable_output = f"Report {report_id} is available [here]({presigned_report_url})"

    return (readable_output, {}, report)


def submit_file_command(client: Client, args: dict[str, Any]) -> tuple[str, dict, dict]:
    entry_id = demisto.args().get("entryID")
    file_entry = demisto.getFilePath(entry_id)  # .get('path')
    file_name = file_entry["name"]
    file_path = file_entry["path"]
    files = {"file": (file_name, open(file_path, "rb"))}

    # Optional parameters to send along with the file
    optional_params = ["password", "param", "screenshot", "video", "fileExtraction", "memoryDump", "pcap"]
    data = {}
    for param in optional_params:
        value = demisto.args().get(param)
        if value:
            data[param] = value

    scan = client.submit_file(files=files, data=data)

    scan["filename"] = file_name
    del scan["status"]
    scan["overall_status"] = "RUNNING"

    report_id = scan.get("report_id")

    readable_output = (
        f"Started analysis of {file_name} with FireEye Detection on Demand."
        f"Results will be published to report id: {report_id}"
    )
    outputs = {"FireEyeDoD.Scan(val.report_id == obj.report_id)": scan}
    return (readable_output, outputs, scan)


def submit_urls_command(client: Client, args: dict[str, Any]) -> tuple[str, dict, dict]:
    urls = argToList(args.get("urls"))
    if len(urls) == 0:
        raise ValueError("hash(es) not specified")

    # Format the URLs into a string list, which the API understands
    formatted_urls = "[" + ",".join([url.replace(url, f'"{url}"') for url in urls]) + "]"
    data = {"urls": formatted_urls}

    scan = client.submit_urls(data=data)

    del scan["status"]
    scan["overall_status"] = "RUNNING"

    report_id = scan.get("report_id")

    readable_output = (
        f"Started analysis of {urls} with FireEye Detection on Demand.Results will be published to report id: {report_id}"
    )
    outputs = {"FireEyeDoD.Scan(val.report_id == obj.report_id)": scan}
    return (readable_output, outputs, scan)


def get_reports_command(client: Client, args: dict[str, Any]) -> tuple[str, dict, Any]:
    report_id_list = argToList(args.get("report_ids", []))
    extended = args.get("extended_report", "False")
    screenshot = args.get("get_screenshot", "false")
    artifact = args.get("get_artifact", "")
    if len(report_id_list) == 0:
        raise ValueError("report_id(s) not specified")

    report_list: list[dict[str, Any]] = []
    for report_id in report_id_list:
        report = client.report_status(report_id=report_id, extended=extended)
        if screenshot.lower() == "true":
            screenshot = client.report_artifact(report_id=report_id, artifact_type="screenshot")
            stored_img = fileResult("screenshot.gif", screenshot)
            demisto.results(
                {
                    "Type": entryTypes["image"],
                    "ContentsFormat": formats["text"],
                    "File": stored_img["File"],
                    "FileID": stored_img["FileID"],
                    "Contents": "",
                }
            )

        if artifact != "":
            artifacts = client.report_artifact(report_id=report_id, artifact_type=artifact)
            stored_artifacts = fileResult("artifacts.zip", artifacts)
            demisto.results(
                {
                    "Type": entryTypes["file"],
                    "ContentsFormat": formats["text"],
                    "File": stored_artifacts["File"],
                    "FileID": stored_artifacts["FileID"],
                    "Contents": "",
                }
            )

        report_list.append(report)

    readable_output = tableToMarkdown("Scan status", report_list)
    outputs = {"FireEyeDoD.Scan(val.report_id == obj.report_id)": report_list}
    return (readable_output, outputs, report_list)


""" MAIN FUNCTION """


def main() -> None:
    """main function, parses params and runs command functions

    :return:
    :rtype:
    """

    api_key = demisto.params().get("apikey")

    # get the service API url
    base_url = demisto.params()["url"]

    # if your Client class inherits from BaseClient, SSL verification is
    # handled out of the box by it, just pass ``verify_certificate`` to
    # the Client constructor
    verify_certificate = not demisto.params().get("insecure", False)

    # if your Client class inherits from BaseClient, system proxy is handled
    # out of the box by it, just pass ``proxy`` to the Client constructor
    proxy = demisto.params().get("proxy", False)

    # INTEGRATION DEVELOPER TIP
    # You can use functions such as ``demisto.debug()``, ``demisto.info()``,
    # etc. to print information in the XSOAR server log. You can set the log
    # level on the server configuration
    # See: https://xsoar.pan.dev/docs/integrations/code-conventions#logging

    demisto.debug(f"Command being called is {demisto.command()}")
    try:
        headers = {"feye-auth-key": f"{api_key}"}
        client = Client(base_url=base_url, verify=verify_certificate, headers=headers, proxy=proxy)

        if demisto.command() == "test-module":
            # This is the call made when pressing the integration Test button.
            result = test_module(client)
            demisto.results(result)

        elif demisto.command() == "fireeye-dod-get-hashes":
            return_outputs(*get_hashes_command(client, demisto.args()))

        elif demisto.command() == "fireeye-dod-get-reports":
            return_outputs(*get_reports_command(client, demisto.args()))

        elif demisto.command() == "fireeye-dod-submit-file":
            return_outputs(*submit_file_command(client, demisto.args()))

        elif demisto.command() == "fireeye-dod-submit-urls":
            return_outputs(*submit_urls_command(client, demisto.args()))

        elif demisto.command() == "fireeye-dod-get-report-url":
            return_outputs(*generate_report_url(client, demisto.args()))

    # Log exceptions and return errors
    except Exception as e:
        raise e
        # demisto.error(traceback.format_exc())  # print the traceback
        # return_error(f'Failed to execute {demisto.command()} command.\nError:\n{str(e)}')


""" ENTRY POINT """


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