MetaDefender Sandbox Deprecated

Deprecated. Use MetaDefender Aether instead.

Forensics & Malware Analysis · MetaDefender Sandbox (Deprecated)

Details

IDMetaDefender Sandbox
ProviderOPSWAT
CategoryForensics & Malware Analysis
From Version6.0.0
Docker Imagedemisto/python3:3.12.8.3296088

README

Unique adaptive threat analysis technology, enabling zero-day malware detection and more Indicator of Compromise (IOCs) extraction. (previously known as OPSWAT Filescan Sandbox)

Configure MetaDefender Sandbox in Cortex

Parameter Required
Server URL (e.g. https://www.filescan.io/api) True
API Key True
Trust any certificate (not secure) False
Use system proxy settings False
Verbose 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.

metadefender-sandbox-scan-url


Scan URL with MetaDefender Sandbox

Note: MetaDefender Sandbox handles URL scanning as a file scan.

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

Base Command

metadefender-sandbox-scan-url

Input

Argument Name Description Required
url The URL to submit Required
timeout The timeout for the polling in seconds Optional
hide_polling_output Hide polling output Optional
description Uploaded file/url description Optional
tags Tags array to propagate Optional
password Custom password, in case uploaded archive is protected Optional
is_private If file should not be available for download by other users Optional

Context Output

Path Type Description
DBotScore.Indicator String The indicator that was tested.
DBotScore.Type String The indicator type.
DBotScore.Vendor String The vendor used to calculate the score.
DBotScore.Score Number The actual score.
File.Name String The full file name.
File.SHA256 String The SHA256 hash of the file.
File.Malicious.Vendor String The vendor that reported the file as malicious.
MetaDefender.Sandbox.Analysis.finalVerdict.verdict String The final verdict.
MetaDefender.Sandbox.Analysis.allTags Unknown All tags.
MetaDefender.Sandbox.Analysis.overallState String Overall state of the scan.
MetaDefender.Sandbox.Analysis.taskReference.name String Name of the main scan task.
MetaDefender.Sandbox.Analysis.taskReference.additionalInfo Unknown Additional informations about the main scan task.
MetaDefender.Sandbox.Analysis.taskReference.ID String ID of the main scan task.
MetaDefender.Sandbox.Analysis.taskReference.state String State of the main scan task.
MetaDefender.Sandbox.Analysis.taskReference.resourceReference Unknown Resource reference of the main scan task.
MetaDefender.Sandbox.Analysis.taskReference.opcount Number Counter.
MetaDefender.Sandbox.Analysis.taskReference.processTime Number processTime.
MetaDefender.Sandbox.Analysis.subtaskReferences Unknown Status of scan subtasks.
MetaDefender.Sandbox.Analysis.allSignalGroups Unknown All signal groups.
MetaDefender.Sandbox.Analysis.resources Unknown Resources.
MetaDefender.Sandbox.Analysis.file.name String The name of the file.
MetaDefender.Sandbox.Analysis.file.hash String The SHA256 of the file.
MetaDefender.Sandbox.Analysis.file.type String The type of the submission.

Command example

!metadefender-sandbox-scan-url url=https://www.test.com

Context Example

{
{
    "DBotScore":
    [
        {
            "Indicator": "1111111111111111111111111111111111111111111111111111111111111111",
            "Score": 1,
            "Type": "file",
            "Vendor": "MetaDefender Sandbox"
        }
    ],
    "File":
    [
        {
            "Name": "https://www.test.com",
            "SHA256": "1111111111111111111111111111111111111111111111111111111111111111"
        }
    ],
    "MetaDefender":
    {
        "Sandbox":
        {
            "Analysis":
            [
                {
                    "finalVerdict":
                    {
                        "verdict": "BENIGN"
                    },
                    "allTags":
                    [
                        {
                            "source": "MEDIA_TYPE",
                            "sourceIdentifier": "12345678",
                            "isRootTag": true,
                            "tag":
                            {
                                "name": "html",
                                "synonyms":
                                [],
                                "descriptions":
                                [],
                                "verdict":
                                {
                                    "verdict": "NO_THREAT",
                                    "threatLevel": 0.1,
                                    "confidence": 1
                                }
                            }
                        }
                    ],
                    "overallState": "success_partial",
                    "taskReference":
                    {
                        "name": "transform-file",
                        "additionalInfo":
                        {
                            "submitName": "https://www.test.com",
                            "submitTime": 1679014774270,
                            "digests":
                            {
                                "SHA-256": "1111111111111111111111111111111111111111111111111111111111111111"
                            }
                        },
                        "ID": "abcd-1234",
                        "state": "SUCCESS",
                        "resourceReference":
                        {
                            "type": "TRANSFORM_FILE",
                            "name": "file",
                            "ID": "abcd-5678"
                        },
                        "opcount": 1,
                        "processTime": 20350
                    },
                    "subtaskReferences":
                    [
                        {
                            "name": "domain-resolve",
                            "additionalInfo": 72,
                            "ID": "12345678",
                            "state": "SUCCESS",
                            "resourceReference":
                            {
                                "type": "DOMAIN_RESOLVE",
                                "name": "domain-resolve",
                                "ID": "123456789"
                            },
                            "opcount": 20,
                            "processTime": 11309
                        }
                    ],
                    "allSignalGroups":
                    [
                        {
                            "identifier": "I000",
                            "description": "OSINT source detected malicious resource",
                            "averageSignalStrength": 0.75,
                            "peakSignalStrength": 0.75,
                            "finalSignalStrength": 0.75,
                            "verdict":
                            {
                                "verdict": "LIKELY_MALICIOUS",
                                "threatLevel": 0.75,
                                "confidence": 1
                            },
                            "allTags":
                            [],
                            "signals":
                            [
                                {
                                    "strength": 0.75,
                                    "isStrictlyBasedOnInputData": false,
                                    "signalReadable": "OSINT provider TEST provider (2/93)",
                                    "additionalInfo": "https://www.google.com",
                                    "originPath": "osint.results.verdict",
                                    "originType": "INPUT_FILE",
                                    "originIdentifier": "1234"
                                }
                            ]
                        }
                    ],
                    "resources":
                    {
                        "00f1e4d6-27fb-45e8-8a02-dc53818044ec":
                        {
                            "resourceReference":
                            {
                                "name": "osint"
                            },
                            "results":
                            []
                        }
                    },
                    "file":
                    {
                        "name": "https://www.test.com",
                        "hash": "1111111111111111111111111111111111111111111111111111111111111111",
                        "type": "other"
                    }
                }
            ]
        }
    }
}

Human Readable Output

Scan Result (digest)

FileHash FileName FileType FinalVerdict SubtaskReferences Tags
1111111111111111111111111111111111111111111111111111111111111111 https://www.test.com other BENIGN osint, url-render, domain-resolve html, png

metadefender-sandbox-scan-file


Scan File with MetaDefender Sandbox

Base Command

metadefender-sandbox-scan-file

Input

Argument Name Description Required
entry_id he War Room entry ID of the file to submit Required
timeout The timeout for the polling in seconds Optional
hide_polling_output Hide polling output Optional
description Uploaded file/url description Optional
tags Tags array to propagate Optional
password Custom password, in case uploaded archive is protected Optional
is_private If file should not be available for download by other users Optional

Context Output

Path Type Description
DBotScore.Indicator String The indicator that was tested.
DBotScore.Type String The indicator type.
DBotScore.Vendor String The vendor used to calculate the score.
DBotScore.Score Number The actual score.
File.Name String The full file name.
File.SHA256 String The SHA256 hash of the file.
File.Malicious.Vendor String The vendor that reported the file as malicious.
MetaDefender.Sandbox.Analysis.finalVerdict.verdict String The final verdict.
MetaDefender.Sandbox.Analysis.allTags Unknown All tags.
MetaDefender.Sandbox.Analysis.overallState String Overall state of the scan.
MetaDefender.Sandbox.Analysis.taskReference.name String Name of the main scan task.
MetaDefender.Sandbox.Analysis.taskReference.additionalInfo Unknown Additional informations about the main scan task.
MetaDefender.Sandbox.Analysis.taskReference.ID String ID of the main scan task.
MetaDefender.Sandbox.Analysis.taskReference.state String State of the main scan task.
MetaDefender.Sandbox.Analysis.taskReference.resourceReference Unknown Resource reference of the main scan task.
MetaDefender.Sandbox.Analysis.taskReference.opcount Number Counter.
MetaDefender.Sandbox.Analysis.taskReference.processTime Number processTime.
MetaDefender.Sandbox.Analysis.subtaskReferences Unknown Status of scan subtasks.
MetaDefender.Sandbox.Analysis.allSignalGroups Unknown All signal groups.
MetaDefender.Sandbox.Analysis.resources Unknown Resources.
MetaDefender.Sandbox.Analysis.file.name String The name of the file.
MetaDefender.Sandbox.Analysis.file.hash String The SHA256 of the file.
MetaDefender.Sandbox.Analysis.file.type String The type of the submission.

Command example

!metadefender-sandbox-scan-file entry_id=1234@abcd-efgh-ijkl-mnop-xyz

Context Example

{
{
    "DBotScore":
    [
        {
            "Indicator": "1111111111111111111111111111111111111111111111111111111111111111",
            "Score": 1,
            "Type": "file",
            "Vendor": "MetaDefender Sandbox"
        }
    ],
    "File":
    [
        {
            "Name": "1234@abcd-efgh-ijkl-mnop-xyz",
            "SHA256": "1111111111111111111111111111111111111111111111111111111111111111"
        }
    ],
    "MetaDefender":
    {
        "Sandbox":
        {
            "Analysis":
            [
                {
                    "finalVerdict":
                    {
                        "verdict": "MALICIOUS"
                    },
                    "allTags":
                    [
                        {
                            "source": "SIGNAL",
                            "sourceIdentifier": "1234",
                            "isRootTag": false,
                            "tag": 
                            {
                                "name": "packed",
                                "synonyms": [],
                                "descriptions": [],
                                "verdict": {
                                    "verdict": "SUSPICIOUS",
                                    "threatLevel": 0.5,
                                    "confidence": 1
                                }
                            }
                        }
                    ],
                    "overallState": "success_partial",
                    "taskReference":
                    {
                        "name": "transform-file",
                        "additionalInfo": {
                            "submitName": "bad_file.exe",
                            "submitTime": 1679011634945,
                            "digests": {
                                "SHA-256": "1111111111111111111111111111111111111111111111111111111111111111"
                            }
                        },
                        "ID": "1234",
                        "state": "SUCCESS",
                        "resourceReference": {
                            "type": "TRANSFORM_FILE",
                            "name": "file",
                            "ID": "0101010101"
                        },
                        "opcount": 1,
                        "processTime": 7180
                    },
                    "subtaskReferences":
                    [
                        {
                            "name": "domain-resolve",
                            "additionalInfo": 72,
                            "ID": "12345678",
                            "state": "SUCCESS",
                            "resourceReference":
                            {
                                "type": "DOMAIN_RESOLVE",
                                "name": "domain-resolve",
                                "ID": "123456789"
                            },
                            "opcount": 20,
                            "processTime": 11309
                        }
                    ],
                    "allSignalGroups":
                    [
                        {
                            "identifier": "Y002",
                            "description": "Matched a malicious YARA rule",
                            "averageSignalStrength": 1,
                            "peakSignalStrength": 1,
                            "finalSignalStrength": 1,
                            "verdict": {
                                "verdict": "MALICIOUS",
                                "threatLevel": 1,
                                "confidence": 1
                            },
                            "allTags": [],
                            "signals": [
                                {
                                    "strength": 1,
                                    "isStrictlyBasedOnInputData": true,
                                    "signalReadable": "Matched YARA with strength \"0.75\"",
                                    "additionalInfo": "PUP_InstallRex_AntiFWb",
                                    "originPath": "file.yaraMatches",
                                    "originType": "INPUT_FILE",
                                    "originIdentifier": "111111111111111111111111111"
                                }
                            ]
                        }
                    ],
                    "resources":
                    {
                        "00f1e4d6-27fb-45e8-8a02-dc53818044ec":
                        {
                            "resourceReference":
                            {
                                "name": "osint"
                            },
                            "results":
                            []
                        }
                    },
                    "file":
                    {
                        "name": "1234@abcd-efgh-ijkl-mnop-xyz",
                        "hash": "1111111111111111111111111111111111111111111111111111111111111111",
                        "type": "other"
                    }
                }
            ]
        }
    }
}

Human Readable Output

Scan Result (digest)

FileHash FileName FileType FinalVerdict SubtaskReferences Tags
1111111111111111111111111111111111111111111111111111111111111111 1234@abcd-efgh-ijkl-mnop-xyz pe MALICIOUS visualization, osint, domain-resolve html, peexe

metadefender-sandbox-search-query


Search for reports. Finds reports and uploaded files by various tokens.

Base Command

metadefender-sandbox-search-query

Input

Argument Name Description Required
query The query string Required
limit Number of total results. Maximum 50 Optional
page Page number, starting from 1 Optional
page_size The page size. Can be 5, 10 or 20 Optional

Context Output

Path Type Description
MetaDefender.Sandbox.Analysis.id String The analysis id.
MetaDefender.Sandbox.Analysis.file.name String The name of the file.
MetaDefender.Sandbox.Analysis.file.sha256 String The SHA256 of the file.
MetaDefender.Sandbox.Analysis.verdict String The final verdict.
MetaDefender.Sandbox.Analysis.state String Overall state of the scan.
MetaDefender.Sandbox.Analysis.date Date The scan date.
MetaDefender.Sandbox.Analysis.file.mime_type String The file MimeType.
MetaDefender.Sandbox.Analysis.file.short_type String The type of the submission.
MetaDefender.Sandbox.Analysis.tags Unknown All tags.

Command example

!metadefender-sandbox-search-query query="834d1dbfab8330ea5f1844f6e905ed0ac19d1033ee9a9f1122ad2051c56783dc"

Context Example

{
    "MetaDefender":
    {
        "Sandbox":
        {
            "Analysis":
            [
                {
                    "id": "b4f92c03-0fc2-4a40-9d34-8f2b05dd240c",
                    "file": {
                        "name": "bad_file.exe",
                        "mime_type": "application/x-msdownload",
                        "short_type": "peexe",
                        "sha256": "834d1dbfab8330ea5f1844f6e905ed0ac19d1033ee9a9f1122ad2051c56783dc",
                    },
                    "state": "success",
                    "verdict": "malicious",
                    "tags": [
                        {
                            "source": "MEDIA_TYPE",
                            "sourceIdentifier": "834d1dbfab8330ea5f1844f6e905ed0ac19d1033ee9a9f1122ad2051c56783dc",
                            "isRootTag": true,
                            "tag": {
                                "name": "peexe",
                                "synonyms": [],
                                "descriptions": [],
                                "verdict": {
                                    "verdict": "NO_THREAT",
                                    "threatLevel": 0.1,
                                    "confidence": 1
                                }
                            }
                        }
                    ],
                    "date": "03/20/2023, 14:28:09"
                }
            ]
        }
    }
}

Human Readable Output

Analysis Result

Id SampleName SHA256 Verdict State Date
8c38be8c-7cfd-4d64-be41-c98a795c9ce0 bad_file.exe 834d1dbfab8330ea5f1844f6e905ed0ac19d1033ee9a9f1122ad2051c56783dc MALICIOUS success_partial 03/14/2023, 15:07:07
e334d27f-e2b1-46c9-9936-7d3155eb3706 bad_file.exe 834d1dbfab8330ea5f1844f6e905ed0ac19d1033ee9a9f1122ad2051c56783dc UNKNOWN success 03/14/2020, 15:03:48

Configuration parameters

  • url — Server URL (e.g. https://www.filescan.io/api) (required)
  • api_key — (required)
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings

Commands (3)

  • metadefender-sandbox-scan-file

    Scan file resource.

  • metadefender-sandbox-scan-url

    Scan URL resource.

  • metadefender-sandbox-search-query

    Search for reports. Finds reports and uploaded files by various tokens.

from CommonServerPython import *
from CommonServerUserPython import *

""" IMPORTS """
from typing import Any
import urllib3

# Disable insecure warnings
urllib3.disable_warnings()

"""GLOBALS/PARAMS """
INTEGRATION_NAME = "MetaDefender Sandbox Integration"
INTEGRATION_CONTEXT_NAME = "MetaDefender.Sandbox"


class Client(BaseClient):
    def __init__(self, api_key: str, base_url: str, proxy: bool, verify: bool):
        super().__init__(base_url=base_url, proxy=proxy, verify=verify)
        self.api_key = api_key

        if self.api_key:
            self._headers = {"X-Api-Key": self.api_key}

    def test_module(self) -> dict:
        """
        Return information about the user. (Need API key)
        """
        request_result = self._http_request(
            method="GET",
            ok_codes=([200]),
            url_suffix="/users/me",
        )
        return request_result

    def post_sample(self, args: dict[str, Any]) -> dict[str, Any]:
        data = {}

        if description := args.get("description"):
            data["description"] = description
        if tags := args.get("tags"):
            data["tags"] = tags
        if password := args.get("password"):
            data["password"] = password
        if is_private := args.get("is_private"):
            data["is_private"] = is_private

        if url := args.get("url"):
            data["url"] = url

            return self._http_request(method="POST", url_suffix="/scan/url", ok_codes=([200]), data=data)

        elif entry_id := args.get("entry_id"):
            try:
                file_entry = demisto.getFilePath(entry_id)
            except Exception as e:
                raise DemistoException(f'Failed to find file entry with id:"{entry_id}". got error: {e}')

            with open(file_entry["path"], "rb") as file:
                return self._http_request(
                    method="POST",
                    url_suffix="/scan/file",
                    ok_codes=([200]),
                    data=data,
                    files={"file": file},
                )
        else:
            raise DemistoException("No file or URL was provided.")

    def get_scan_result(self, flow_id: str) -> dict[str, Any]:
        filters = [
            "filter=general",
            "filter=finalVerdict",
            "filter=allTags",
            "filter=overallState",
            "filter=taskReference",
            "filter=subtaskReferences",
            "filter=allSignalGroups",
        ]

        postfix = "&".join(filters)

        url_suffix = f"/scan/{flow_id}/report?{postfix}"

        response = self._http_request(
            method="GET",
            ok_codes=([200]),
            url_suffix=url_suffix,
        )

        return response

    def get_search_query(self, query_string: str, page: int, page_size: int) -> dict[str, Any]:
        return self._http_request(
            method="GET",
            ok_codes=([200]),
            params={"query": query_string, "page_size": page_size, "page": page},
            url_suffix="/reports/search",
        )


""" HELPER FUNCTIONS """


def build_one_reputation_result(report: dict[str, Any]):
    score = Common.DBotScore.NONE

    final_verdict = report.get("finalVerdict", {})
    verdict = final_verdict.get("verdict")
    if verdict.upper() == "BENIGN" or verdict.upper() == "INFORMATIONAL" or verdict.upper() == "NO_THREAT":
        score = Common.DBotScore.GOOD
    elif verdict.upper() == "MALICIOUS" or verdict.upper() == "LIKELY_MALICIOUS":
        score = Common.DBotScore.BAD
    elif verdict.upper() == "SUSPICIOUS":
        score = Common.DBotScore.SUSPICIOUS

    report_file = report.get("file", {})
    report_hash = report_file.get("hash", None)

    dbot_score = Common.DBotScore(
        indicator=report_hash,
        indicator_type=DBotScoreType.FILE,
        integration_name="MetaDefender Sandbox",
        score=score,
    )

    file = Common.File(name=report_file.get("name"), sha256=report_hash, dbot_score=dbot_score)

    tags = [tag.get("tag", {}).get("name") for tag in report.get("allTags", [])]
    subtasks = [subtask.get("name") for subtask in report.get("subtaskReferences", [])]
    human_readable = {
        "FileName": report_file.get("name"),
        "FileHash": report_hash,
        "FileType": report_file.get("type"),
        "FinalVerdict": report.get("finalVerdict", {}).get("verdict"),
        "Tags": tags,
        "SubtaskReferences": subtasks,
    }
    readable_output = tableToMarkdown("Scan Result (digest):", human_readable)

    results = CommandResults(
        outputs_prefix=f"{INTEGRATION_CONTEXT_NAME}.Analysis",
        outputs_key_field="sha256",
        outputs=report,
        raw_response=report,
        readable_output=readable_output,
        indicator=file,
    )
    return results


def build_serach_query_result(analyses: list[dict]) -> CommandResults:
    def build_analysis_hr(analysis: dict[str, Any]) -> dict[str, Any]:
        file_result = analysis.get("file", {})
        hr_analysis = {
            "Id": analysis.get("id"),
            "SampleName": file_result.get("name"),
            "SHA256": file_result.get("sha256"),
            "Verdict": analysis.get("verdict"),
            "State": analysis.get("state"),
            "Date": analysis.get("date"),
            "MIMEType": file_result.get("mime_type"),
            "Type": file_result.get("short_type"),
            "Tags": analysis.get("tags"),
        }
        return hr_analysis

    def build_indicator_object(analysis: dict[str, Any]):
        score = Common.DBotScore.NONE

        verdict = analysis.get("verdict", "UNKNOWN")
        if verdict.upper() == "BENIGN" or verdict.upper() == "INFORMATIONAL" or verdict.upper() == "NO_THREAT":
            score = Common.DBotScore.GOOD
        elif verdict.upper() == "MALICIOUS" or verdict.upper() == "LIKELY_MALICIOUS":
            score = Common.DBotScore.BAD
        elif verdict.upper() == "SUSPICIOUS":
            score = Common.DBotScore.SUSPICIOUS

        analysis_file = analysis.get("file", {})
        dbot_score = Common.DBotScore(
            indicator=analysis_file.get("sha256"),
            indicator_type=DBotScoreType.FILE,
            integration_name="MetaDefender Sandbox",
            score=score,
        )

        file = Common.File(
            name=analysis_file.get("name"),
            sha256=analysis_file.get("sha256"),
            dbot_score=dbot_score,
        )

        results = CommandResults(
            outputs_prefix=f"{INTEGRATION_CONTEXT_NAME}.Analysis",
            outputs_key_field="sha256",
            indicator=file,
        )
        return results

    hr_headers = [
        "Id",
        "SampleName",
        "SHA256",
        "Verdict",
        "State",
        "Date",
        "MIMEType",
        "Type",
        "Tags",
    ]

    hr_analysis_ls = []

    for analysis in analyses:
        hr_analysis_ls.append(build_analysis_hr(analysis))

    command_result = CommandResults(
        outputs=analyses,
        readable_output=tableToMarkdown("Analysis Result:", hr_analysis_ls, hr_headers),
        outputs_prefix=f"{INTEGRATION_CONTEXT_NAME}.Analysis",
    )

    return command_result


def sample_submission(client: Client, args: dict[str, Any]) -> PollResult:
    res = client.post_sample(args)
    partial_res = CommandResults(readable_output=f'Waiting for submission "{res.get("flow_id")}" to finish...')
    return PollResult(
        response=CommandResults(
            outputs=res,
            outputs_prefix=f"{INTEGRATION_CONTEXT_NAME}.Submission",
            outputs_key_field="flow_id",
        ),
        args_for_next_run={"flow_id": res.get("flow_id"), **args},
        continue_to_poll=True,
        partial_result=partial_res,
    )


def build_reputation_result(api_reponse: dict[str, Any]):
    reports = api_reponse.get("reports", [])
    command_res_ls = []
    for report in reports:
        command_res_ls.append(build_one_reputation_result(reports[report]))
    return command_res_ls


def is_valid_pass(api_response: dict[str, Any]):
    if "rejected_files" not in api_response:
        return True
    return all(reject.get("rejected_reason") != "INVALID_PASSWORD" for reject in api_response["rejected_files"])


@polling_function(
    name=demisto.command(),
    timeout=arg_to_number(demisto.args().get("timeout", 600)),
    interval=1,
    poll_message="Polling result",
    requires_polling_arg=False,
)
def polling_submit_command(args: dict[str, Any], client: Client):
    if flow_id := args.get("flow_id"):
        api_response = client.get_scan_result(flow_id)
        successful_response = False

        if api_response.get("allFinished", False):
            successful_response = True

        if successful_response:
            if not is_valid_pass(api_response):
                raise DemistoException("Invalid password!")

            return PollResult(response=build_reputation_result(api_response), continue_to_poll=False)

        return PollResult(
            response=[
                CommandResults(
                    outputs=api_response,
                    outputs_prefix=f"{INTEGRATION_CONTEXT_NAME}.Submission",
                    outputs_key_field="flow_id",
                    readable_output=f'Waiting for submission "{api_response.get("flow_id")}" to finish...',
                ),
            ],
            continue_to_poll=True,
            args_for_next_run={"flow_id": args.get("flow_id"), **args},
        )
    else:
        return sample_submission(client, args)


""" COMMANDS """


def test_module_command(client: Client, *_) -> str:
    """Performs a basic GET request to check if the API is reachable and authentication is successful."""
    results = client.test_module()
    if "accountId" in results:
        return "ok"
    raise DemistoException(f"\nTest module failed, {results}")


def scan_command(client: Client, args: dict[str, Any]):
    return polling_submit_command(args=args, client=client)


def search_query_command(client: Client, args: dict[str, Any]):
    def validate_args():
        if page_size and page_size not in [5, 10, 20]:
            raise DemistoException("Page size value must be 5, 10 or 20")
        if page and page <= 0:
            raise DemistoException("Page must be an integer and grater than 0")
        if limit and (limit <= 0 or limit > 50):
            raise DemistoException("Limit must be an integer and between 1 and 50")

    items = []
    query_string = args.get("query", "")
    page_size = arg_to_number(args.get("page_size"))
    page = arg_to_number(args.get("page"))
    limit = arg_to_number(args.get("limit")) or 10

    validate_args()

    if page_size and not page:
        page = 1
    elif not page_size and page:
        page_size = 10

    if page_size and page:
        items = client.get_search_query(query_string, page, page_size).get("items", [])
    else:
        page_size = 20
        page = 1
        continue_query = True
        while continue_query:
            response = client.get_search_query(query_string, page, page_size)
            actual_items = response.get("items", [])
            total_available_items = response.get("count", len(items))
            # queried all or reached the limit
            if total_available_items == len(items) or len(items) >= limit:
                continue_query = False
            items += actual_items
            page += 1
        items = items[0:limit]

    if items:
        return build_serach_query_result(items)
    return CommandResults(readable_output="No Results were found.")


""" COMMANDS MANAGER / SWITCH PANEL """


def main():
    params = demisto.params()
    base_url = params.get("url", "")
    api_key = params.get("api_key", {}).get("password")
    verify = not params.get("insecure", False)
    proxy = params.get("proxy", False)

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

    try:
        client = Client(api_key=api_key, base_url=base_url, verify=verify, proxy=proxy)
        handle_proxy()

        if command == "test-module":
            return_results(test_module_command(client))
        elif command == "metadefender-sandbox-scan-url":
            return_results(scan_command(client, args))
        elif command == "metadefender-sandbox-scan-file":
            return_results(scan_command(client, args))
        elif command == "metadefender-sandbox-search-query":
            return_results(search_query_command(client, args))
        else:
            raise NotImplementedError(f"{command} command is not implemented.")
    except Exception as e:
        err_msg = f"Exception in {INTEGRATION_NAME} : Failed to execute {command} command: [{e!r}]"
        return_error(err_msg, error=e)


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