Secneurx Analysis

Fully automated malware dynamic analysis sandboxing.

Forensics & Malware Analysis · SecneurX Analysis

Details

IDSecneurx Analysis
ProviderSecneurX
CategoryForensics & Malware Analysis
From Version6.5.0
Docker Imagedemisto/python3:3.12.13.10116658
Supported ModulesAgentix XSIAM EDR Cortex Cloud Cloud Runtime Security

README

Use SecneurX Analysis pack to provide threat analysts and incident response teams with the advanced malware isolation and inspection environment needed to safely execute advanced malware samples, and understand their behavior.
You can use the pack’s playbooks to detonate both files and URLs. SecneurX Analysis performs both static and dynamic analysis of advanced threats, including zero day and targeted attacks.

This integration was integrated and tested with version 1.0.0 of SecneurX Analysis

Configure SecneurX Analysis in Cortex

Parameter Description Required
Server URL Input the url of SecneurX Analysis server. True
API Key Input the API key to access the sandbox. True
Trust any certificate (not secure)   False
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.

snx-analysis-get-verdict


Get verdict summary report of the analyzed sample

Base Command

snx-analysis-get-verdict

Input

Argument Name Description Required
task_uuid Input the Task UUID value obtained as response from submission. Required
polling Use Cortex XSOAR built-in polling to retrieve the result when it’s ready. Default is False. Optional
interval Frequency that the polling command will run (seconds). Default is set to “30” Optional
timeout Amount of time to poll before declaring a timeout and resuming the playbook (in seconds). Default is set “600”. Optional

Context Output

Path Type Description
SecneurXAnalysis.Verdict.task_uuid String Task UUID is unique id of analyzed sample
SecneurXAnalysis.Verdict.verdict String Verdict is summary result of analyzed sample
SecneurXAnalysis.Verdict.sha256 String SHA256 value find from analyzed sample
SecneurXAnalysis.Verdict.file_name String File Name of analyzed sample
SecneurXAnalysis.Verdict.status String Analysis queued sample state
SecneurXAnalysis.Verdict.submission_time String Analysis queued sample submission time value.

Human Readable Output

sha256 status submission_time task_uuid url verdict
2323714b7571c9c87e71799499d577126a487ff58177247e5b67a83a866f83a5 Completed 2022-07-22 07:37:10 2323714b7571c9c87e71799499d577126a487ff58177247e5b67a83a866f83a5-2022-07-22-07-37-10 https://google.com Clean

snx-analysis-get-completed


Get the list of submitted samples whose status is marked as “Completed”

Base Command

snx-analysis-get-completed

Input

Argument Name Description Required
last_hours Optional. Allows you to specify the number of hours. The value should be as number of hours (e.g. 5) - Lists all the queued samples submitted in the last 5 hrs. Optional
last_count Optional. Allows you to specify the max no.of queued samples to list. The value should be number type (e.g. 50) - Lists the last submitted 50 samples that are queued. Optional

Context Output

There is no context output for this command.

Human Readable Output

task_uuid verdict file_name report_available
ce5869808c1c4e99c7df7122118d06f0b38a7f302d5f5504a419626336156182-2022-07-22-07-45-21 No Threats   true

snx-analysis-get-pending


Get the list of submitted samples that are still in pending state

Base Command

snx-analysis-get-pending

Input

Argument Name Description Required
last_hours Optional. Allows you to specify the number of hours. The value should be as number of hours (e.g. 5) - Lists all the queued samples submitted in the last 5 hrs. Optional
last_count Optional. Allows you to specify the max no.of queued samples to list. The value should be number type (e.g. 50) - Lists the last submitted 50 samples that are queued. Optional

Context Output

There is no context output for this command.

Human Readable Output

task_uuid file_name status sha256
4f751e74f7d05e6ebc27de36caa03c889b3d6bb57755aacd454bbce63a0da313-2022-07-21-15-28-20 4f751e74f7d05e6ebc27de36caa03c889b3d6bb57755aacd454bbce63a0da313 Analyzing 4f751e74f7d05e6ebc27de36caa03c889b3d6bb57755aacd454bbce63a0da313

snx-analysis-get-status


Get the status of all the submitted samples

Base Command

snx-analysis-get-status

Input

Argument Name Description Required
last_hours Optional. Allows you to specify the number of hours. The value should be as number of hours (e.g. 5) - Lists all the queued samples submitted in the last 5 hrs. Optional
last_count Optional. Allows you to specify the max no.of queued samples to list. The value should be number type (e.g. 50) - Lists the last submitted 50 samples that are queued. Optional

Context Output

There is no context output for this command.

Human Readable Output

task_uuid file_name status sha256
ce5869808c1c4e99c7df7122118d06f0b38a7f302d5f5504a419626336156182-2022-07-22-07-45-21 sample.exe Completed ce5869808c1c4e99c7df7122118d06f0b38a7f302d5f5504a419626336156182
2323714b7571c9c87e71799499d577126a487ff58177247e5b67a83a866f83a5-2022-07-22-07-37-10 sample_2.dll Completed 2323714b7571c9c87e71799499d577126a487ff58177247e5b67a83a866f83a5

snx-analysis-submit-file


Submit a file for Analysis

Base Command

snx-analysis-submit-file

Input

Argument Name Description Required
EntryID Entry ID value of upload file. Required
Platform Type the OS platform on which the file to be analysed. Default is set to Windows7. Possible values are Windows7, Windows10, Android, Ubuntu. Optional
Priority Type the priority of the sample for analysis. Default is set to Normal. Possible values are High, Normal. Optional
Duration Type the duration of the analysis in seconds. Not all malicious programs are active right after the launch. Some of them take time to fully reveal the attack vectors. Default is set to 120. Possible values are 120, 180, 240, 300. Optional
Extension If you want the file for submission to be treated and analysed as a specific file extension, mention it. Optional
Reboot Reboot the system during the analysis. Default is set to ‘False’. Possible values are True, False. Optional
File Password If the file for submission is protected with a user-defined password, please enter the password for our system to open and detonate it. Optional

Context Output

Path Type Description
SecneurXAnalysis.SubmitFile.task_uuid String Task UUID is unique ID for submitted file. Use this ID for get the report and verdict.
SecneurXAnalysis.SubmitFile.submission_time String Submission Time Created at the time the file was submitted

Human Readable Output

task_uuid submission_time
2323714b7571c9c87e71799499d577126a487ff58177247e5b67a83a866f83a5-2022-07-22-07-27-42 2022-07-22 07:27:42

snx-analysis-submit-url


Submit the URL for Analysis

Base Command

snx-analysis-submit-url

Input

Argument Name Description Required
URL Input the URL for analysis. Required
Priority Type the priority of the sample for analysis. Default is set to Normal. Possible values are High, Normal. Possible values are: High, Normal. Optional
Duration Type the duration of the analysis in seconds. Not all malicious programs are active right after the launch. Some of them take time to fully reveal the attack vectors. Default is set to 120. Possible values are 120, 180, 240, 300. Possible values are: 120, 180, 240, 300. Optional
Reboot Reboot the system during the analysis. Default is set to ‘False’. Possible values are True, False. Possible values are: True, False. Optional

Context Output

Path Type Description
SecneurXAnalysis.SubmitURL.task_uuid String Task UUID is the unique ID for the submitted file. Use this ID to get the report.
SecneurXAnalysis.SubmitURL.submission_time String Submission Time Created at the time the file was submitted

Human Readable Output

task_uuid submission_time
2323714b7571c9c87e71799499d577126a487ff58177247e5b67a83a866f8fff-2022-07-22-07-12-15 2022-07-22 07:12:15

snx-analysis-get-report


Get the detailed report of the analyzed sample.

Base Command

snx-analysis-get-report

Input

Argument Name Description Required
task_uuid Input the Task UUID value obtained as response from submission. Required
report_format Mention the output format of the report. The value should be “json” or “html”. The default is set to “json”. Possible values are: html, json. Default is json. Optional

Context Output

Path Type Description
SecneurXAnalysis.Report.SHA256 String SHA256 value of the analyzed sample
SecneurXAnalysis.Report.Platform String Platform of the analyzed sample
SecneurXAnalysis.Report.Verdict String Summary result of the analyzed sample
SecneurXAnalysis.Report.Tags String More details of the analyzed sample
SecneurXAnalysis.Report.DnsRequests String List of DNS data observed in the analyzed sample
SecneurXAnalysis.Report.HttpRequests String List of HTTP data observed in the analyzed sample
SecneurXAnalysis.Report.JA3Digests String List of JA3 data observed in the analyzed sample
SecneurXAnalysis.Report.ProcessCreated String Process behaviour data observed in the analyzed sample
SecneurXAnalysis.Report.RegistrySet String List of Registry creations observed in the analyzed sample
SecneurXAnalysis.Report.RegistryDeleted String List of Registry deletions observed in the analyzed sample
SecneurXAnalysis.Report.FileCreated String List of File creations observed in the analyzed sample
SecneurXAnalysis.Report.FileDropped String List of File drops observed in the analyzed sample
SecneurXAnalysis.Report.FileDeleted String List of File deletions observed in the analyzed sample
SecneurXAnalysis.Report.FileModified String List of File changes observed in the analyzed sample
SecneurXAnalysis.Report.IOC String List of IOC’s observed in the analyzed sample
SecneurXAnalysis.Report.Status String Analysis queued sample state

snx-analysis-get-quota


Get the API Key quota usage details.

Base Command

snx-analysis-get-quota

Context Output

Path Type Description
SecneurXAnalysis.Quota.start_time String Creation Time of the API Key
SecneurXAnalysis.Quota.used Integer Used count of API Key
SecneurXAnalysis.Quota.allowed Integer Limitation count of API Key
SecneurXAnalysis.Quota.scale String API Key expiration renew scale type
SecneurXAnalysis.Quota.unused String Unused count of API Key

Human Readable Output

allowed scale start_time unused used
100 MONTH 01 Sep 2022 00:00:00 UTC 60 40

Configuration parameters

  • url — Server URL (required)
  • apiKey — API Key (required)
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings

Commands (8)

  • snx-analysis-get-completed

    Get the list of submitted samples whose status is marked as "Completed".

  • snx-analysis-get-pending

    Get the list of submitted samples that are still in pending state.

  • snx-analysis-get-quota

    Get the API Key quota usage details.

  • snx-analysis-get-report

    Get the detailed report of the analyzed sample.

  • snx-analysis-get-status

    Get the status of all the submitted samples.

  • snx-analysis-get-verdict

    Get verdict summary report of the analyzed sample.

  • snx-analysis-submit-file

    Submit a file for Analysis.

  • snx-analysis-submit-url

    Submit the URL for Analysis.

import json  # noqa
import traceback  # noqa
from collections import OrderedDict  # noqa
from typing import Any, Dict  # noqa

import demistomock as demisto
from CommonServerPython import *  # noqa # pylint: disable=unused-wildcard-import

# Disable insecure warnings
DATE_FORMAT = "%Y-%m-%dT%H:%M:%SZ"  # ISO8601 format with UTC, default in XSOAR

SNX_IOC_TYPES_TO_DEMISTO_TYPES = {
    "url": FeedIndicatorType.URL,
    "md5": FeedIndicatorType.File,
    "sha-1": FeedIndicatorType.File,
    "sha-256": FeedIndicatorType.File,
    "ipv4-addr": FeedIndicatorType.IP,
    "domain": FeedIndicatorType.Domain,
    "ipv6-addr": FeedIndicatorType.IPv6,
    "email-addr": FeedIndicatorType.Email,
    "domain-name": FeedIndicatorType.Domain,
    "file:hashes.MD5": FeedIndicatorType.File,
}

SNX_VERDICT_TO_DBOTSCORE = {
    "No Threats": Common.DBotScore.GOOD,
    "Suspicious": Common.DBotScore.SUSPICIOUS,
    "Malware": Common.DBotScore.BAD,
    "Ransomware": Common.DBotScore.BAD,
}


class Client(BaseClient):
    """Implement class for SecneurX Analysis sandbox"""

    def get_response(self, urlSuffix: str, paramsDict: dict[str, str]):
        try:
            if urlSuffix == "/get_report":
                respType = "text"
            else:
                respType = "json"
            return self._http_request(method="GET", url_suffix=urlSuffix, params=paramsDict, resp_type=respType, timeout=90), None

        except Exception as e:
            return None, e

    def submit_file(self, urlSuffix: str, fileData: dict[str, Any], paramsDict: dict[str, str]):
        try:
            return self._http_request(method="POST", url_suffix=urlSuffix, files=fileData, params=paramsDict, timeout=90), None

        except Exception as e:
            return None, e

    def submit_url(self, urlSuffix: str, paramsDict: dict[str, str], urlParams: dict[str, str]):
        try:
            return self._http_request(method="POST", url_suffix=urlSuffix, data=urlParams, params=paramsDict, timeout=90), None

        except Exception as e:
            return None, e


class SNXReportParser:
    JSON_URL = "url"
    JSON_IOC = "IOC"
    JSON_KEY = "key"
    JSON_DATA = "data"
    JSON_TYPE = "type"
    JSON_VALUE = "value"
    JSON_OBJECTS = "objects"
    JSON_PATTERN = "pattern"
    JSON_PLATFORM = "platform"
    JSON_HOSTNAME = "hostname"
    JSON_DNS_REQ = "dnsLookups"
    JSON_TAGS = "ArtifactsTags"
    JSON_FILE_SHA256 = "sha256"
    JSON_JA3_LIST = "ja3Digests"
    JSON_METHOD = "requestMethod"
    JSON_RESOLVEDIPS = "resolvedIps"
    JSON_VERDICTS = "ArtifactsVerdict"
    JSON_FILE_WRITTEN = "filesWritten"
    JSON_FILE_DROPPED = "filesDropped"
    JSON_FILE_DELETED = "filesDeleted"
    JSON_FILE_MODIFIED = "fileModified"
    JSON_HTTP_REQ = "httpConversations"
    JSON_ANALYSIS_TIME = "completedTime"
    JSON_REGISTRY_SET = "registryKeysSet"
    JSON_SHA256 = "analysisSubjectSha256"
    JSON_STATUS_CODE = "responseStatusCode"
    JSON_PROCESS_CREATED = "processesCreated"
    JSON_REGISTRY_DELETED = "registryKeysDeleted"
    SNX_SUBMISSION_NAME_KEY = "analysisSubjectName"
    SNX_SUBMISSION_TYPE_KEY = "analysisSubjectType"


class JsonTableParser:
    SNX_URL = "URL"
    SNX_TAGS = "Tags"
    SNX_SHA256 = "SHA256"
    SNX_METHOD = "Method"
    SNX_VERDICT = "Verdict"
    SNX_DNS_REQ = "DNSRequests"
    SNX_JA3_LIST = "JA3Digests"
    SNX_PLATFORM_KEY = "Platform"
    SNX_HTTP_REQ = "HTTPRequests"
    SNX_STATUS_CODE = "StatusCode"
    SNX_REGISTRY_SET = "RegistrySet"
    SNX_FILE_WRITTEN = "FileCreated"
    SNX_FILE_DROPPED = "FileDropped"
    SNX_FILE_DELETED = "FileDeleted"
    SNX_FILE_MODIFIED = "FileModified"
    SNX_ANALYSIS_TIME = "SubmissionTime"
    SNX_PROCESS_CREATED = "ProcessCreated"
    SNX_REGISTRY_DELETED = "RegistryDeleted"


class SNXResponse:
    FAILED = 0
    SUCCESS = 1
    SNX_URL_KEY = "url"
    SNX_MSG_KEY = "msg"
    SNX_IOC_KEY = "IOC"
    SNX_FILE_KEY = "file"
    SNX_NULL_KEY = "NULL"
    SAMPLE_KEY = "Sample"
    POLLING_KEY = "polling"
    TIMEOUT_KEY = "timeout"
    SNX_RESULT_KEY = "data"
    INTERVAL_KEY = "interval"
    SNX_SHA256_KEY = "sha256"
    SNX_FAILED_KEY = "Failed"
    SNX_STATUS_KEY = "status"
    SNX_MALWARE_KEY = "Malware"
    SNX_VERDICT_KEY = "verdict"
    SNX_SUCCESS_KEY = "success"
    SNX_REBOOT_KEY = "reboot_on"
    SNX_WINDOWS_KEY = "Windows7"
    SNX_PLATFORM_KEY = "platform"
    SNX_PRIORITY_KEY = "priority"
    SNX_DURATION_KEY = "duration"
    SNX_FILENAME_KEY = "file_name"
    SNX_ERROR_MSG_KEY = "error_msg"
    SNX_TASK_UUID_KEY = "task_uuid"
    SNX_COMPLETED_KEY = "Completed"
    SNX_EXTENSTION_KEY = "extension"
    SNX_LAST_HOURS_KEY = "last_hours"
    SNX_LAST_COUNT_KEY = "last_count"
    SNX_PROVIDER = "SecneurX Analysis"
    SNX_REPORT_KEY = "report_available"
    SNX_REPORT_FORMAT_KEY = "report_format"
    SNX_FILE_PWD_KEY = "compressed_password"


class SNXErrorMsg:
    SUCCESS_MSG = "ok"
    CONFIG_ERR = "Configuration Error"
    INVALID_ERR = "Endpoint Error: Invalid Server URL"
    FAILED_ERR = "Submit Error: Sample submittion failed"
    AUTH_ERR = "Authorization Error: make sure API Key is correctly set"
    SERVER_ERR = "Endpoint Error: Invalid Server URL (or) Invalid input parameters values"
    NOT_FOUND_ERR = "Report could not be generated"


def test_module(client: Client) -> Any:
    get_status_cmd(client, {SNXResponse.SNX_LAST_COUNT_KEY: "2"})
    return SNXErrorMsg.SUCCESS_MSG


def create_request_json(argsDict: dict[str, str]) -> dict:
    params = {}
    try:
        if SNXResponse.SNX_TASK_UUID_KEY in argsDict:
            params[SNXResponse.SNX_TASK_UUID_KEY] = argsDict.get(SNXResponse.SNX_TASK_UUID_KEY)
        if SNXResponse.SNX_LAST_COUNT_KEY in argsDict:
            params[SNXResponse.SNX_LAST_COUNT_KEY] = argsDict.get(SNXResponse.SNX_LAST_COUNT_KEY)
        if SNXResponse.SNX_LAST_HOURS_KEY in argsDict:
            params[SNXResponse.SNX_LAST_HOURS_KEY] = argsDict.get(SNXResponse.SNX_LAST_HOURS_KEY)
        if "Platform" in argsDict:
            platformValue = argsDict.get("Platform")
            params[SNXResponse.SNX_PLATFORM_KEY] = str(platformValue)
        if "Priority" in argsDict:
            priorityValue = argsDict.get("Priority")
            params[SNXResponse.SNX_PRIORITY_KEY] = str(priorityValue)
        if "Extension" in argsDict:
            extnValue = argsDict.get("Extension")
            if extnValue is not None and len(extnValue) != 0:
                params[SNXResponse.SNX_EXTENSTION_KEY] = argsDict.get("Extension")
        if "Duration" in argsDict:
            durationValue = argsDict.get("Duration")
            if durationValue is not None and len(durationValue) != 0:
                params[SNXResponse.SNX_DURATION_KEY] = argsDict.get("Duration")
        if "File Password" in argsDict:
            pwdValue = argsDict.get("File Password")
            if pwdValue is not None and len(pwdValue) != 0:
                params[SNXResponse.SNX_FILE_PWD_KEY] = pwdValue
        if "Reboot" in argsDict:
            params[SNXResponse.SNX_REBOOT_KEY] = argsDict.get("Reboot")
        if SNXResponse.SNX_REPORT_FORMAT_KEY in argsDict:
            params[SNXResponse.SNX_REPORT_FORMAT_KEY] = argsDict.get(SNXResponse.SNX_REPORT_FORMAT_KEY)

    except Exception as e:
        demisto.error(e)
    return params


def error_response(err_msg) -> str:
    msg = None
    try:
        if err_msg.res.status_code == 401:
            msg = SNXErrorMsg.AUTH_ERR
        elif err_msg.res.status_code == 400:
            msg = SNXErrorMsg.SERVER_ERR
        elif err_msg.res.status_code == 404:
            msg = SNXErrorMsg.NOT_FOUND_ERR
        elif err_msg.res.status_code == 500:
            msg = SNXErrorMsg.FAILED_ERR
        else:
            msg = SNXErrorMsg.CONFIG_ERR
    except Exception:
        msg = SNXErrorMsg.INVALID_ERR
    return msg


def parse_response(response):
    try:
        jsonContent = OrderedDict()
        if SNXReportParser.JSON_SHA256 in response:
            jsonContent[JsonTableParser.SNX_SHA256] = response[SNXReportParser.JSON_SHA256]
        if SNXReportParser.JSON_PLATFORM in response:
            jsonContent[JsonTableParser.SNX_PLATFORM_KEY] = response[SNXReportParser.JSON_PLATFORM]
        if SNXReportParser.JSON_ANALYSIS_TIME in response:
            jsonContent[JsonTableParser.SNX_ANALYSIS_TIME] = response[SNXReportParser.JSON_ANALYSIS_TIME]
        if SNXReportParser.JSON_VERDICTS in response:
            verdictResult = None
            verdictValue = response[SNXReportParser.JSON_VERDICTS]
            verdictResult = verdictValue.lower().capitalize()
            jsonContent[JsonTableParser.SNX_VERDICT] = verdictResult
        if SNXReportParser.JSON_TAGS in response:
            jsonContent[JsonTableParser.SNX_TAGS] = response[SNXReportParser.JSON_TAGS]
        if SNXReportParser.JSON_DNS_REQ in response:
            dnsList = []
            for dnsData in response[SNXReportParser.JSON_DNS_REQ]:
                dnsReq = []
                if SNXReportParser.JSON_HOSTNAME in dnsData:
                    dnsReq.append(dnsData[SNXReportParser.JSON_HOSTNAME])
                if SNXReportParser.JSON_RESOLVEDIPS in dnsData:
                    dnsReq.append(dnsData[SNXReportParser.JSON_RESOLVEDIPS])
                data = formatCell(dnsReq)
                dnsList.append(data)
            if dnsList:
                jsonContent[JsonTableParser.SNX_DNS_REQ] = dnsList
        if SNXReportParser.JSON_HTTP_REQ in response:
            httpList = []
            split_line = ""
            for httpData in response[SNXReportParser.JSON_HTTP_REQ]:
                methodValue = None
                requestValue = None
                statusCodeValue = None
                if SNXReportParser.JSON_METHOD in httpData:
                    methodValue = httpData[SNXReportParser.JSON_METHOD]
                if SNXReportParser.JSON_URL in httpData:
                    requestValue = httpData[SNXReportParser.JSON_URL]
                if SNXReportParser.JSON_STATUS_CODE in httpData:
                    statusCodeValue = httpData[SNXReportParser.JSON_STATUS_CODE]
                if methodValue and requestValue and statusCodeValue:
                    httpList.append(
                        f"{split_line}[" + methodValue + "] " + requestValue + " [Status : " + str(statusCodeValue) + "]"
                    )
                    split_line = "\n"
            if httpList:
                jsonContent[JsonTableParser.SNX_HTTP_REQ] = httpList
        if SNXReportParser.JSON_JA3_LIST in response:
            jsonContent[JsonTableParser.SNX_JA3_LIST] = response[SNXReportParser.JSON_JA3_LIST]
        if SNXReportParser.JSON_PROCESS_CREATED in response:
            creationList = convert_json_to_str(response[SNXReportParser.JSON_PROCESS_CREATED])
            jsonContent[JsonTableParser.SNX_PROCESS_CREATED] = creationList
        if SNXReportParser.JSON_REGISTRY_SET in response:
            registrySetList = []
            split_line = ""
            for registry_data in response[SNXReportParser.JSON_REGISTRY_SET]:
                keyData = registry_data[SNXReportParser.JSON_KEY]
                registrySetList.append(f"{split_line}" + keyData)
                split_line = "\n"
            if registrySetList:
                jsonContent[JsonTableParser.SNX_REGISTRY_SET] = registrySetList
        if SNXReportParser.JSON_REGISTRY_DELETED in response:
            jsonContent[JsonTableParser.SNX_REGISTRY_DELETED] = response[SNXReportParser.JSON_REGISTRY_DELETED]
        if SNXReportParser.JSON_FILE_WRITTEN in response:
            fileCreatedList = convert_json_to_str(response[SNXReportParser.JSON_FILE_WRITTEN])
            jsonContent[JsonTableParser.SNX_FILE_WRITTEN] = fileCreatedList
        if SNXReportParser.JSON_FILE_DROPPED in response:
            file_drop_list = []
            for file_drop in response[SNXReportParser.JSON_FILE_DROPPED]:
                sha256Value = file_drop[SNXReportParser.JSON_FILE_SHA256]
                typeValue = file_drop[SNXReportParser.JSON_TYPE]
                file_drop_list.append(typeValue + " : " + sha256Value)
            if file_drop_list:
                jsonContent[JsonTableParser.SNX_FILE_DROPPED] = file_drop_list
        if SNXReportParser.JSON_FILE_DELETED in response:
            fileDeletedList = convert_json_to_str(response[SNXReportParser.JSON_FILE_DELETED])
            jsonContent[JsonTableParser.SNX_FILE_DELETED] = fileDeletedList
        if SNXReportParser.JSON_FILE_MODIFIED in response:
            fileModifiedList = convert_json_to_str(response[SNXReportParser.JSON_FILE_MODIFIED])
            jsonContent[JsonTableParser.SNX_FILE_MODIFIED] = fileModifiedList
        if SNXReportParser.JSON_IOC in response and SNXReportParser.JSON_DATA in response[SNXReportParser.JSON_IOC]:
            iocList = parse_report_iocs(response[SNXReportParser.JSON_IOC][SNXReportParser.JSON_DATA])
            jsonContent[SNXResponse.SNX_IOC_KEY] = iocList
        return jsonContent

    except Exception as e:
        raise DemistoException(e)


def convert_json_to_str(data_list):
    formated_list = []
    try:
        split_line = ""
        for data in data_list:
            formatValue = json.dumps(data)
            formatValue = formatValue.rstrip('"').lstrip('"')
            formated_list.append(f"{split_line}" + formatValue)
            split_line = "\n"
    except Exception as e:
        raise DemistoException(e)
    return formated_list


def parse_report_iocs(ioc_json):
    parsed_ioc_list = []
    try:
        if SNXReportParser.JSON_OBJECTS in ioc_json:
            ioc_list = ioc_json[SNXReportParser.JSON_OBJECTS]
            for ioc_data in ioc_list:
                if SNXReportParser.JSON_PATTERN in ioc_data:
                    patternData = ioc_data[SNXReportParser.JSON_PATTERN]
                    patternData = patternData.replace("[", "").replace("]", "")
                    patternKey = patternData.split(":")[0]
                    patternValue = patternData.split(" = ")[1].replace("'", "")
                    if patternKey.lower() in SNX_IOC_TYPES_TO_DEMISTO_TYPES:
                        patternKey = SNX_IOC_TYPES_TO_DEMISTO_TYPES[patternKey]
                    parsed_ioc_list.append(patternKey + " : " + str(patternValue))

    except Exception as e:
        raise DemistoException(e)
    return parsed_ioc_list


def format_report_contents(contents):
    try:

        def dict_to_string(nested_dict):
            return json.dumps(nested_dict).lstrip("{").rstrip("}").replace("'", "").replace('"', "")

        table_contents = OrderedDict()
        for key, val in contents.items():
            if isinstance(val, dict):
                table_contents[key] = dict_to_string(val)
            elif isinstance(val, list):
                table_values = []
                for item in val:
                    if isinstance(item, dict):
                        table_values.append(dict_to_string(item))
                    else:
                        table_values.append(item)
                table_contents[key] = table_values
            else:
                table_contents[key] = val
        return table_contents
    except Exception as e:
        raise DemistoException(e)


def parse_dbot_score(reportJson):
    dbotScore = None
    try:
        if reportJson:
            submissionType = reportJson.get(SNXReportParser.SNX_SUBMISSION_TYPE_KEY, None)
            verdictValue = reportJson.get(SNXReportParser.JSON_VERDICTS, None)
            verdictScore = 0
            if verdictValue is not None and verdictValue in SNX_VERDICT_TO_DBOTSCORE:
                verdictScore = SNX_VERDICT_TO_DBOTSCORE[verdictValue]
            if submissionType == SNXResponse.SNX_FILE_KEY:
                indicatorValue = reportJson.get(SNXReportParser.JSON_SHA256, None)
                if indicatorValue:
                    dbotScore = Common.DBotScore(
                        indicator=indicatorValue,
                        indicator_type=DBotScoreType.FILE,
                        score=verdictScore,
                        integration_name=SNXResponse.SNX_PROVIDER,
                    )
            else:
                indicatorValue = reportJson.get(SNXReportParser.SNX_SUBMISSION_NAME_KEY, None)
                if indicatorValue:
                    dbotScore = Common.DBotScore(
                        indicator=indicatorValue,
                        indicator_type=DBotScoreType.URL,
                        score=verdictScore,
                        integration_name=SNXResponse.SNX_PROVIDER,
                    )

    except Exception as e:
        raise DemistoException(e)
    return dbotScore


def parse_report_entity(reportJson):
    dbot_score = parse_dbot_score(reportJson)
    indicator = None
    try:
        if reportJson and dbot_score:
            submissionType = reportJson.get(SNXReportParser.SNX_SUBMISSION_TYPE_KEY, None)
            verdictValue = reportJson.get(SNXReportParser.JSON_VERDICTS, None)
            tagList = reportJson.get(SNXReportParser.JSON_TAGS, verdictValue)
            sha256Value = reportJson.get(SNXReportParser.JSON_SHA256, None)
            subjectName = reportJson.get(SNXReportParser.SNX_SUBMISSION_NAME_KEY, None)
            if subjectName:
                if submissionType == SNXResponse.SNX_FILE_KEY:
                    indicator = Common.File(
                        name=subjectName, dbot_score=dbot_score, sha256=sha256Value, tags=tagList, description=verdictValue
                    )
                elif submissionType == SNXResponse.SNX_URL_KEY:
                    indicator = Common.URL(url=subjectName, dbot_score=dbot_score, tags=tagList, description=verdictValue)  # type: ignore

    except Exception as e:
        raise DemistoException(e)
    return indicator


def post_submit_file(client: Client, args: dict[str, str]) -> CommandResults:
    urlSuffix = "/submit_file"
    entryId = args.get("EntryID") or None
    if entryId is None:
        raise DemistoException("Entry ID Not Found")
    platformValue = args.get("platform") or SNXResponse.SNX_WINDOWS_KEY
    params = create_request_json(args)
    if "platform" not in params:
        params["platform"] = platformValue
    fileEntry = demisto.getFilePath(entryId)
    fileName = fileEntry["name"]
    filePath = fileEntry["path"]
    fileData = {"file": (fileName, open(filePath, "rb"))}
    response, err_msg = client.submit_file(urlSuffix, fileData, params)
    if response:
        if SNXResponse.SNX_SUCCESS_KEY in response and SNXResponse.SNX_RESULT_KEY in response:
            finalJson = response[SNXResponse.SNX_RESULT_KEY]
            readableOutput = tableToMarkdown(f"File Submitted Successfully: {fileName}", finalJson)
            return CommandResults(readable_output=readableOutput, outputs_prefix="SecneurXAnalysis.SubmitFile", outputs=finalJson)
        else:
            readableOutput = tableToMarkdown(f"File Submission Failed: {fileName}", response)
            return CommandResults(readable_output=readableOutput, outputs_prefix="SecneurXAnalysis.SubmitFile", outputs=response)
    else:
        msg = error_response(err_msg)
        outputJson = {SNXResponse.SNX_ERROR_MSG_KEY: msg}
        readableOutput = tableToMarkdown(f"File Submission Failed: {fileName}", t=outputJson)
        return CommandResults(readable_output=readableOutput, outputs_prefix="SecneurXAnalysis.SubmitFile", outputs=outputJson)


def post_submit_url(client: Client, args: dict[str, str]) -> CommandResults:
    urlSuffix = "/analyze_url"
    urlValue = args.get("URL") or None
    if urlValue is None or len(urlValue) == 0:
        raise DemistoException("Input url value is empty")
    params = create_request_json(args)
    urlParams = {SNXReportParser.JSON_URL: urlValue}
    response, err_msg = client.submit_url(urlSuffix, params, urlParams)
    if response:
        if SNXResponse.SNX_SUCCESS_KEY in response and SNXResponse.SNX_RESULT_KEY in response:
            finalJson = response[SNXResponse.SNX_RESULT_KEY]
            readableOutput = tableToMarkdown("URL Submitted Successfuly", finalJson)
            return CommandResults(readable_output=readableOutput, outputs_prefix="SecneurXAnalysis.SubmitURL", outputs=finalJson)
        else:
            readableOutput = tableToMarkdown("URL Submission Failed", response)
            return CommandResults(readable_output=readableOutput, outputs_prefix="SecneurXAnalysis.SubmitURL", outputs=response)
    else:
        msg = error_response(err_msg)
        outputJson = {SNXResponse.SNX_ERROR_MSG_KEY: msg}
        readableOutput = tableToMarkdown("URL Submission Failed", t=outputJson)
        return CommandResults(readable_output=readableOutput, outputs_prefix="SecneurXAnalysis.SubmitURL", outputs=outputJson)


def get_verdict_cmd(client: Client, args: dict[str, str]) -> CommandResults:
    taskUuid = args.get(SNXResponse.SNX_TASK_UUID_KEY) or None
    if taskUuid is None:
        raise DemistoException("Task UUID Parameter value is not found")
    else:
        urlSuffix = "/get_verdict"
        params = {SNXResponse.SNX_TASK_UUID_KEY: taskUuid}
        response, err_msg = client.get_response(urlSuffix, params)
        if response:
            if SNXResponse.SNX_SUCCESS_KEY in response and response[SNXResponse.SNX_SUCCESS_KEY] == SNXResponse.SUCCESS:
                dataResult = response[SNXResponse.SNX_RESULT_KEY]
                readableOutput = tableToMarkdown(f"SecneurX Analysis - Verdict Result: {taskUuid}", t=dataResult)
                return CommandResults(
                    readable_output=readableOutput,
                    outputs=dataResult,
                    outputs_key_field="task_uuid",
                    outputs_prefix="SecneurXAnalysis.Verdict",
                    raw_response=dataResult,
                )
            else:
                readableOutput = tableToMarkdown(f"SecneurX Analysis - Verdict Result: {taskUuid}", t=response)
                return CommandResults(
                    readable_output=readableOutput,
                    outputs={"Status": SNXResponse.SNX_FAILED_KEY},
                    outputs_key_field="task_uuid",
                    outputs_prefix="SecneurXAnalysis.Verdict",
                    raw_response=response,
                )
        else:
            msg = error_response(err_msg)
            outputJson = {SNXResponse.SNX_ERROR_MSG_KEY: msg, "Status": SNXResponse.SNX_FAILED_KEY}
            readableOutput = tableToMarkdown("SecneurX Analysis - Error", t=outputJson)
            return CommandResults(
                readable_output=readableOutput,
                outputs=outputJson,
                outputs_prefix="SecneurXAnalysis.Verdict",
                outputs_key_field="task_uuid",
            )


def get_completed_cmd(client: Client, args: dict[str, str]) -> CommandResults:
    urlSuffix = "/get_completed"
    params = create_request_json(args)
    response, err_msg = client.get_response(urlSuffix, params)
    if response:
        if SNXResponse.SNX_SUCCESS_KEY in response and response[SNXResponse.SNX_SUCCESS_KEY] == SNXResponse.SUCCESS:
            reportList = response.get(SNXResponse.SNX_RESULT_KEY, SNXResponse.SNX_NULL_KEY)
            if reportList != SNXResponse.SNX_NULL_KEY and len(reportList) > 0:
                readableOutput = tableToMarkdown(
                    "SecneurX Analysis - List of Completed Samples:",
                    t=reportList,
                    headers=[
                        SNXResponse.SNX_TASK_UUID_KEY,
                        SNXResponse.SNX_VERDICT_KEY,
                        SNXResponse.SNX_STATUS_KEY,
                        SNXResponse.SNX_REPORT_KEY,
                    ],
                )
                return CommandResults(
                    readable_output=readableOutput, outputs_prefix="SecneurXAnalysis.Completed", raw_response=reportList
                )
            else:
                msgJson = {"msg": "No samples to display"}
                readableOutput = tableToMarkdown("SecneurX Analysis - List of Completed Samples: ", msgJson)
                return CommandResults(
                    readable_output=readableOutput, outputs_prefix="SecneurXAnalysis.Completed", outputs=msgJson
                )
        else:
            readableOutput = tableToMarkdown("", response)
            return CommandResults(readable_output=readableOutput, outputs_prefix="SecneurXAnalysis.Completed", outputs=response)
    else:
        msg = error_response(err_msg)
        raise DemistoException(msg)


def get_pending_cmd(client: Client, args: dict[str, str]) -> CommandResults:
    urlSuffix = "/get_processing"
    params = create_request_json(args)
    response, err_msg = client.get_response(urlSuffix, params)
    if response:
        if SNXResponse.SNX_SUCCESS_KEY in response and response[SNXResponse.SNX_SUCCESS_KEY] == SNXResponse.SUCCESS:
            reportList = response.get(SNXResponse.SNX_RESULT_KEY, SNXResponse.SNX_NULL_KEY)
            if reportList != SNXResponse.SNX_NULL_KEY and len(reportList) > 0:
                for report in reportList:
                    if SNXResponse.SNX_FILENAME_KEY in report:
                        report[SNXResponse.SAMPLE_KEY] = report[SNXResponse.SNX_FILENAME_KEY]
                    elif SNXReportParser.JSON_URL in report:
                        report[SNXResponse.SAMPLE_KEY] = report[SNXReportParser.JSON_URL]
                    else:
                        continue
                readableOutput = tableToMarkdown(
                    "SecneurX Analysis - List of Samples in Pending State: ",
                    t=reportList,
                    headers=[
                        SNXResponse.SNX_TASK_UUID_KEY,
                        SNXResponse.SAMPLE_KEY,
                        SNXResponse.SNX_STATUS_KEY,
                        SNXResponse.SNX_SHA256_KEY,
                    ],
                )
                return CommandResults(
                    readable_output=readableOutput, outputs_prefix="SecneurXAnalysis.Pending", raw_response=reportList
                )
            else:
                msgJson = {"msg": "No samples to display"}
                readableOutput = tableToMarkdown("SecneurX Analysis - List of Samples in Pending State:", msgJson)
                return CommandResults(readable_output=readableOutput, outputs_prefix="SecneurXAnalysis.Pending", outputs=msgJson)
        else:
            readableOutput = tableToMarkdown("", response)
            return CommandResults(readable_output=readableOutput, outputs_prefix="SecneurXAnalysis.Pending", outputs=response)
    else:
        msg = error_response(err_msg)
        raise DemistoException(msg)


def get_status_cmd(client: Client, args: dict[str, str]) -> CommandResults:
    urlSuffix = "/get_status"
    params = create_request_json(args)
    response, err_msg = client.get_response(urlSuffix, params)
    if response:
        if SNXResponse.SNX_SUCCESS_KEY in response and response[SNXResponse.SNX_SUCCESS_KEY] == SNXResponse.SUCCESS:
            reportList = response.get(SNXResponse.SNX_RESULT_KEY, SNXResponse.SNX_NULL_KEY)
            if reportList != SNXResponse.SNX_NULL_KEY and len(reportList) > 0:
                for report in reportList:
                    if SNXResponse.SNX_FILENAME_KEY in report:
                        report[SNXResponse.SAMPLE_KEY] = report[SNXResponse.SNX_FILENAME_KEY]
                    elif SNXReportParser.JSON_URL in report:
                        report[SNXResponse.SAMPLE_KEY] = report[SNXReportParser.JSON_URL]
                    else:
                        continue
                readableOutput = tableToMarkdown(
                    "SecneurX Analysis - Status of Submitted Samples:",
                    t=reportList,
                    headers=[
                        SNXResponse.SNX_TASK_UUID_KEY,
                        SNXResponse.SAMPLE_KEY,
                        SNXResponse.SNX_STATUS_KEY,
                        SNXResponse.SNX_SHA256_KEY,
                    ],
                )
                return CommandResults(
                    readable_output=readableOutput, outputs_prefix="SecneurXAnalysis.Status", raw_response=reportList
                )
            else:
                msgJson = {"msg": "No samples to display"}
                readableOutput = tableToMarkdown("SecneurX Analysis - Status of Submitted Samples: ", msgJson)
                return CommandResults(readable_output=readableOutput, outputs_prefix="SecneurXAnalysis.Status", outputs=msgJson)
        else:
            readableOutput = tableToMarkdown("", response)
            return CommandResults(readable_output=readableOutput, outputs_prefix="SecneurXAnalysis.Status", outputs=response)

    else:
        msg = error_response(err_msg)
        raise DemistoException(msg)


def get_report_cmd(client: Client, args: dict[str, str]):
    urlSuffix = "/get_report"
    taskUuid = args.get(SNXResponse.SNX_TASK_UUID_KEY) or None
    reportFormat = args.get(SNXResponse.SNX_REPORT_FORMAT_KEY) or "json"
    if reportFormat is None or (reportFormat != "html" and reportFormat != "json"):
        raise DemistoException("Invalid value of report file format paramater")
    if taskUuid is None:
        raise DemistoException("Task Uuid Parameter value is not found")
    elif len(taskUuid) <= 10:
        raise DemistoException("Invalid Task Uuid value")
    else:
        reportExtn = "." + reportFormat
        params = create_request_json(args)
        response, err_msg = client.get_response(urlSuffix, params)
        if response:
            if reportFormat == "json":
                resJson = json.loads(response)
                contents = parse_response(resJson)
                indicator = parse_report_entity(resJson)
                title = None
                headerList = []
                readableContents = None
                for header in contents:
                    headerList.append(header)
                title = f"SecneurX Analysis - Detailed Report of the Analyzed Sample: {taskUuid}"
                readableContents = format_report_contents(contents)
                readableOutputs = tableToMarkdown(title, readableContents, headers=headerList, headerTransform=pascalToSpace)
                reportFileName = taskUuid + reportExtn
                fileContent = fileResult(reportFileName, response)
                return_results(fileContent)
                return CommandResults(
                    readable_output=readableOutputs,
                    indicator=indicator,
                    outputs=contents,
                    outputs_prefix="SecneurXAnalysis.Report",
                    raw_response=resJson,
                )
            else:
                reportFileName = taskUuid + reportExtn
                fileContent = fileResult(reportFileName, response)
                demisto.results(fileContent)  # noqa: RET503
        else:
            msg = error_response(err_msg)
            result = {SNXResponse.SNX_ERROR_MSG_KEY: msg, "Status": SNXResponse.SNX_FAILED_KEY}
            readableOutputs = tableToMarkdown(f"SecneurX Analysis - Failed: {taskUuid}", result)
            return CommandResults(readable_output=readableOutputs, outputs_prefix="SecneurXAnalysis.Report", outputs=result)


def get_quota_cmd(client: Client) -> CommandResults:
    urlSuffix = "/get_quota"
    response, err_msg = client.get_response(urlSuffix, {})
    if response:
        if response.get(SNXResponse.SNX_SUCCESS_KEY) == SNXResponse.SUCCESS:
            quotaData = response[SNXResponse.SNX_RESULT_KEY]
            readableOutput = tableToMarkdown("SecneurX Analysis - API Key Quota Usage:", t=quotaData)
            return CommandResults(
                readable_output=readableOutput, outputs=quotaData, outputs_prefix="SecneurXAnalysis.Quota", raw_response=response
            )
        else:
            readableOutput = tableToMarkdown("SecneurX Analysis - API Key Quota Usage:", t=response)
            return CommandResults(
                readable_output=readableOutput, outputs=response, outputs_prefix="SecneurXAnalysis.Quota", raw_response=response
            )
    else:
        msg = error_response(err_msg)
        raise DemistoException(msg)


def main():
    apiKey = demisto.params().get("apiKey")
    baseUrl = urljoin(demisto.params().get("url"), "/api/v1")
    verifyCertificate = not demisto.params().get("insecure", False)
    proxy = demisto.params().get("proxy", False)
    headers = {"api-key": apiKey}
    client = Client(base_url=baseUrl, verify=verifyCertificate, headers=headers, proxy=proxy)
    cmdAction = demisto.command()
    demisto.debug(f"Command being called is {cmdAction}")
    try:
        if cmdAction == "test-module":
            result = test_module(client)
            return_results(result)

        elif cmdAction == "snx-analysis-get-verdict":
            return_results(get_verdict_cmd(client, demisto.args()))

        elif cmdAction == "snx-analysis-get-completed":
            return_results(get_completed_cmd(client, demisto.args()))

        elif cmdAction == "snx-analysis-get-pending":
            return_results(get_pending_cmd(client, demisto.args()))

        elif cmdAction == "snx-analysis-get-status":
            return_results(get_status_cmd(client, demisto.args()))

        elif cmdAction == "snx-analysis-submit-file":
            return_results(post_submit_file(client, demisto.args()))

        elif cmdAction == "snx-analysis-submit-url":
            return_results(post_submit_url(client, demisto.args()))

        elif cmdAction == "snx-analysis-get-report":
            return_results(get_report_cmd(client, demisto.args()))

        elif cmdAction == "snx-analysis-get-quota":
            return_results(get_quota_cmd(client))

    except Exception as e:
        demisto.error(traceback.format_exc())  # print the traceback
        return_error(f"Failed to execute {cmdAction} command.\nError:\n{e!s}")


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