Trend Micro Vision One

TrendAI Vision One™ is a purpose-built threat defense platform that provides added value and new benefits beyond XDR solutions, allowing you to see more and respond faster. Providing deep and broad extended detection and response (XDR) capabilities that collect and automatically correlate data across multiple security layers—email, endpoints, servers, cloud workloads, and networks—TrendAI Vision One™ prevents the majority of attacks with automated protection.

Data Enrichment & Threat Intelligence · TrendAI Vision One™

Details

IDTrend Micro Vision One
ProviderTrendAI™
CategoryData Enrichment & Threat Intelligence
From Version6.2.0
Docker Imagedemisto/python3:3.12.13.10116658
Supported ModulesAgentix XSIAM

README

Integration Author: TrendAI™

Support and maintenance for this integration are provided by the author. Please use the following contact details:


TrendAI Vision One™ is a purpose-built threat defense platform that provides added value and new benefits beyond XDR solutions, allowing you to see more and respond faster. Providing deep and broad extended detection and response (XDR) capabilities that collect and automatically correlate data across multiple security layers—email, endpoints, servers, cloud workloads, and networks—TrendAI Vision One™ prevents the majority of attacks with automated protection.

Configure TrendAI Vision One™ on Cortex XSOAR

  1. Navigate to Settings > Integrations > Servers & Services.
  2. Search for Trend.
  3. Click Add instance to create and configure a new integration instance.

    Parameter Description Required
    API URL (e.g. https://api.xdr.trendmicro.com) The base url for the TrendAI Vision One™ API True
    API Key The API token to access data True
    Fetch incidents   False
    Incidents Fetch Interval   False
    Incident type   False
    Sync On First Run (days)   False
    Max Incidents   False
    Use system proxy settings   False
    Trust any certificate (not secure)   False
  4. Click Test to validate the URLs, token, and connection.

Commands

You can execute these commands from the Cortex XSOAR CLI, as part of an automation, or in a playbook.
After you successfully execute a command, a DBot message appears in the War Room with the command details.

trendmicro-visionone-add-to-block-list


Adds a file SHA-1, IP address, domain, or URL object to the User-Defined Suspicious Objects List, which blocks the objects on subsequent detections

Base Command

  1. trendmicro-visionone-add-to-block-list

Input

Argument Name Description Required
value_type The type of object you would like to add to the block list: “file_sha1”, “ip”, “domain”, “url” or “mailbox”. Possible values are: file_sha1, domain, ip, url, mailbox. Required
target_value The object you would like to add that matches the value-type. Required
product_id Target product. Optional
description Optional description for reference. Optional

Context Output

Path Type Description
VisionOne.BlockList.actionId string Action ID of task adding file SHA-1, IP address, domain, or URL to the User-Defined Suspicious Objects List
VisionOne.BlockList.taskStatus string Task status of adding file SHA-1, IP address, domain, or URL object to the User-Defined Suspicious Objects List

Note: To get the complete task status run polling command trendmicro-visionone-check-task-status giving actionId as input parameter.

trendmicro-visionone-remove-from-block-list


Removes a file SHA-1, IP address, domain, or URL from the User-Defined Suspicious Objects List

Base Command

  1. trendmicro-visionone-remove-from-block-list

Input

Argument Name Description Required
value_type The type of object you would like to remove from the block list: “file_sha1”, “ip”, “domain”, “url” or “mailbox”. Possible values are: file_sha1, domain, ip, url, mailbox. Required
target_value The object you would like to add that matches the value-type. Required
product_id Target product. Optional
description Optional description for reference. Optional

Context Output

Path Type Description
VisionOne.BlockList.actionId string Action ID of task removing file SHA-1, IP address, domain, or URL object from the User-Defined Suspicious Objects List
VisionOne.BlockList.taskStatus string Task Status of removing file SHA-1, IP address, domain, or URL object that was added to the User-Defined Suspicious Objects List from block list

Note: To get the complete task status run polling command trendmicro-visionone-check-task-status giving actionId as input parameter.

trendmicro-visionone-quarantine-email-message


Moves a message from a mailbox to the quarantine folder

Base Command

  1. trendmicro-visionone-quarantine-email-message

Input

Argument Name Description Required
message_id Email Message ID from TrendAI Vision One™ message activity data. Required
mailbox Email mailbox where the message will be quarantined from. Required
message_delivery_time Email message’s original delivery time. Required
product_id Target product. Default is sca. Optional
description Optional description for reference. Optional

Context Output

Path Type Description
VisionOne.Email.actionId string The Action Id of moving a message from a mailbox to the quarantine folder
VisionOne.Email.taskStatus string The status of moving a message from a mailbox to the quarantine folder

Note: To get the complete task status run polling command trendmicro-visionone-check-task-status giving actionId as input parameter.

trendmicro-visionone-delete-email-message


Deletes a message from a mailbox

Base Command

  1. trendmicro-visionone-delete-email-message

Input

Argument Name Description Required
message_id Email Message ID from TrendAI Vision One™ One message activity data. Required
mailbox Email mailbox where the message will be quarantined from. Required
message_delivery_time Email message’s delivery time. Required
product_id Target product. Default is sca. Optional
description Optional description for reference. Optional

Context Output

Path Type Description
VisionOne.Email.actionId string The action id of deleting a message from a mailbox
VisionOne.Email.taskStatus string The task status of deleting a message from a mailbox

Note: To get the complete task status run polling command trendmicro-visionone-check-task-status giving actionId as input parameter.

trendmicro-visionone-isolate-endpoint


Disconnects an endpoint from the network (but allows communication with the managing TrendAI™ product)

Base Command

  1. trendmicro-visionone-isolate-endpoint

Input

Argument Name Description Required
endpoint “hostname”, “macaddr” or “ip” of the endpoint to isolate. Required
product_id Target product: “sao”, “sds”, or “xes”. Possible values are: sao, sds, xes. Default is sao. Required
description Description. Optional

Context Output

Path Type Description
VisionOne.Endpoint_Connection.actionId string The action ID of isolate endpoint task
VisionOne.Endpoint_Connection.taskStatus string The task status of isolate endpoint

Note: To get the complete task status run polling command trendmicro-visionone-check-task-status giving actionId as input parameter.
Note: The above command should be added with execution timeout in the advanced field of playbook execution. The recommended timeout be 20 minutes.

trendmicro-visionone-restore-endpoint-connection


Restores network connectivity to an endpoint that applied the “isolate endpoint” action

Base Command

  1. trendmicro-visionone-restore-endpoint-connection

Input

Argument Name Description Required
endpoint “hostname”, “macaddr” or “ip” of the endpoint to restore. Required
product_id Target product: “sao”, “sds”, or “xes”. Possible values are: sao, sds, xes. Default is sao. Required
description Description. Optional

Context Output

Path Type Description
VisionOne.Endpoint_Connection.actionId string The action ID of the restore endpoint connection
VisionOne.Endpoint_Connection.taskStatus string The task status of restore endpoint connection

Note: To get the complete task status run polling command trendmicro-visionone-check-task-status giving actionId as input parameter.
Note: The above command should be added with execution timeout in the advanced field of playbook execution. The recommended timeout be 20 minutes.

trendmicro-visionone-add-objects-to-exception-list


Adds domains, file SHA-1 values, IP addresses, or URLs to the Exception List and prevents these objects from being added to the Suspicious Object List

Base Command

  1. trendmicro-visionone-add-objects-to-exception-list

Input

Argument Name Description Required
type Object type: “domain”, “ip”, “sha1”, or “url”. Possible values are: domain, ip, sha1, url. Required
value The object value. Full and partial matches supported. Domain partial match, (with a wildcard as the subdomain, example, .example.com) IP partial match, (IP range example, 192.168.35.1-192.168.35.254, cidr example, 192.168.35.1/24) URL Partial match, (Supports wildcards ‘http://.’’, ‘https://.’’ at beginning, or ‘’’ at the end. Multiple wild cards also supported, such as , https://.example.com/path1/) SHA1 Only full match”. Required
description Exception description. Optional

Context Output

Path Type Description
VisionOne.Exception_List.message string status message success after task completion
VisionOne.Exception_List.status_code string status code of response
VisionOne.Exception_List.total_items string count of item present in exception list

trendmicro-visionone-delete-objects-from-exception-list


Deletes domains, file SHA-1 values, IP addresses, or URLs from the Exception List.

Base Command

  1. trendmicro-visionone-delete-objects-from-exception-list

Input

Argument Name Description Required
type Object type: “domain”, “ip”, “sha1”, or “url”. Possible values are: domain, ip, sha1, url. Required
value The object value. Required

Context Output

Path Type Description
VisionOne.Exception_List.message string status message success after task completion
VisionOne.Exception_List.status_code number status code of response
VisionOne.Exception_List.total_items string count of item present in exception list

trendmicro-visionone-add-objects-to-suspicious-list


Adds domains, file SHA-1 values, IP addresses, or URLs to the Suspicious Object List.

Base Command

  1. trendmicro-visionone-add-objects-to-suspicious-list

Input

Argument Name Description Required
type Object type: “domain”, “ip”, “sha1”, or “url”. Possible values are: domain, ip, sha1, url. Required
value The object value. Required
description Description. Optional
scan_action The action to take if object is found. If you don’t use this parameter, the scan action specified in default_settings.riskLevel.type will be used instead. “block” or “log”. Possible values are: block, log. Optional
risk_level The Suspicious Object risk level. If you don’t use this parameter, high will be used instead. “high”, “medium” or “low”. Possible values are: high, medium, low. Optional
expiry_days The number of days to keep the object in the Suspicious Object List. If you don’t use this parameter, the default_settings.expiredDay scan action will be used instead. Optional

Context Output

Path Type Description
VisionOne.Suspicious_List.message string Status message of adding item to suspicious object list
VisionOne.Suspicious_List.status_code number Response code of adding item to suspicious object list
VisionOne.Suspicious_List.total_items number Number of items present in suspicious object list

trendmicro-visionone-delete-objects-from-suspicious-list


Deletes domains, file SHA-1 values, IP addresses, or URLs from the Suspicious Object List

Base Command

  1. trendmicro-visionone-delete-objects-from-suspicious-list

Input

Argument Name Description Required
type Object type: “domain”, “ip”, “sha1”, or “url”. Possible values are: domain, ip, sha1, url. Required
value The object value. Required

Context Output

Path Type Description
VisionOne.Suspicious_List.message string Status message of removing item from suspicious object list
VisionOne.Suspicious_List.status_code number Response code of removing item from suspicious object list
VisionOne.Suspicious_List.total_items number Number of items present in suspicious object list

trendmicro-visionone-get-endpoint-info


Retrieves information about a specific endpoint

Base Command

  1. trendmicro-visionone-get-endpoint-info

Input

Argument Name Description Required
endpoint “hostname”, “macaddr” or “ip” of the endpoint to query. Required

Context Output

Path Type Description
VisionOne.Endpoint_Info.message string Message information from the request
VisionOne.Endpoint_Info.errorCode integer Error code
VisionOne.Endpoint_Info.status string Status of the request
VisionOne.Endpoint_Info.logonAccount string Account currently logged on to the endpoint
VisionOne.Endpoint_Info.hostname string Hostname
VisionOne.Endpoint_Info.macAddr string MAC address
VisionOne.Endpoint_Info.ip string IP address
VisionOne.Endpoint_Info.osName string Operating System name
VisionOne.Endpoint_Info.osVersion string Operating System nersion
VisionOne.Endpoint_Info.osDescription string Description of the Operating System
VisionOne.Endpoint_Info.productCode string Product code of the TrendAI™ product running on the endpoint

trendmicro-visionone-terminate-process


Terminates a process that is running on an endpoint

Base Command

  1. trendmicro-visionone-terminate-process

Input

Argument Name Description Required
endpoint “hostname”, “macaddr” or “ip” of the endpoint to terminate process on. Required
file_sha1 SHA1 hash of the process to terminate. Required
product_id Target product. Possible values are: sao. Default is sao. Optional
description Description. Optional
filename Optional file name list for log. Optional

Context Output

Path Type Description
VisionOne.Terminate_Process.actionId string Action Id of the current running task
VisionOne.Terminate_Process.taskStatus string Status of current running task

Note: To get the complete task status run polling command trendmicro-visionone-check-task-status giving actionId as input parameter.
Note: The above command should be added with execution timeout in the advanced field of playbook execution. The recommended timeout is 20 minutes.

trendmicro-visionone-get-file-analysis-status


Retrieves the status of a sandbox analysis submission

Base Command

  1. trendmicro-visionone-get-file-analysis-status

Input

Argument Name Description Required
task_id task_id from the trendmicro-visionone-submit-file-to-sandbox command output. Required

Context Output

Path Type Description
VisionOne.File_Analysis_Status.message string Status of the sandbox analysis
VisionOne.File_Analysis_Status.code string Response code
VisionOne.File_Analysis_Status.task_id string task_id of the task queried
VisionOne.File_Analysis_Status.taskStatus string Sandbox analysis status
VisionOne.File_Analysis_Status.digest string The hash values of file analyzed
VisionOne.File_Analysis_Status.analysis_completion_time string Sample analysis completed time.
VisionOne.File_Analysis_Status.risk_level string Risk Level of the analyzed file.
VisionOne.File_Analysis_Status.descritption string Scan result description for NotAnalyzed.
VisionOne.File_Analysis_Status.detection_name_list unknown Detection name of this sample, if applicable.
VisionOne.File_Analysis_Status.threat_type_list unknown Threat type of this sample.
VisionOne.File_Analysis_Status.file_type string File type of this sample.
VisionOne.File_Analysis_Status.report_id string ID used to get the report and suspicious object. Empty means no report.
VisionOne.File_Analysis_Status.DBotScore.score number The DBot score.
VisionOne.File_Analysis_Status.DBotScore.Vendor string The Vendor name.
VisionOne.File_Analysis_Status.DBotScore.Reliability string The reliability level.

trendmicro-visionone-get-file-analysis-report


Retrieves the analysis report, investigation package, or Suspicious Object List of a submitted file

Base Command

  1. trendmicro-visionone-get-file-analysis-report

Input

Argument Name Description Required
report_id report_id of the sandbox submission retrieved from the trendmicro-visionone-get-file-analysis-status command. Required
type Type of report to retrieve: “vaReport”, “investigationPackage”, or “suspiciousObject”. Possible values are: vaReport, investigationPackage, suspiciousObject. Required

Context Output

Path Type Description
VisionOne.File_Analysis_Report.message string Status message of file report
VisionOne.File_Analysis_Report.code string status code of file report
VisionOne.File_Analysis_Report.type string Suspicious object type
VisionOne.File_Analysis_Report.value string Suspicious object value
VisionOne.File_Analysis_Report.risk_level string Risk Level of suspicious object
VisionOne.File_Analysis_Report.analysis_completion_time string Analyze time of suspicious object
VisionOne.File_Analysis_Report.expired_time string Expire time of suspicious object
VisionOne.File_Analysis_Report.root_file_sha1 string Sample sha1 generate this suspicious object

trendmicro-visionone-collect-forensic-file


Compresses a file on an endpoint in a password-protected archive and then sends the archive to the XDR service platform

Base Command

  1. trendmicro-visionone-collect-forensic-file

Input

Argument Name Description Required
endpoint “hostname”, “macaddr” or “ip” of the endpoint to collect file from. Required
product_id Product: “sao”, “sds” or “xes”. Possible values are: sao, xes, sds. Required
file_path Path to the file to collect. Required
os Type of OS. “windows”, “mac” or “linux”. Required
description Description of the file. Optional

Context Output

Path Type Description
VisionOne.Collect_Forensic_File.actionId string Action ID of the particular file.
VisionOne.Collect_Forensic_File.taskStatus string Task status of collected file

Note: To get the complete task status run polling command trendmicro-visionone-check-task-status giving actionId as input parameter.
Note: The above command should be added with execution timeout in the advanced field of playbook execution. The recommended timeout be 20 minutes.

trendmicro-visionone-download-information-for-collected-forensic-file


Retrieves a URL and other information required to download a collected file via the trendmicro-visionone-collect-forensic-file command

Base Command

  1. trendmicro-visionone-download-information-for-collected-forensic-file

Input

Argument Name Description Required
actionId actionId output from the collect command used to collect the file. Required

Context Output

Path Type Description
VisionOne.Download_Information_For_Collected_Forensic_File.url string URL of the collected file
VisionOne.Download_Information_For_Collected_Forensic_File.expires string URL expiration date
VisionOne.Download_Information_For_Collected_Forensic_File.password string Archive password for the protected forensic file
VisionOne.Download_Information_For_Collected_Forensic_File.filename string Name of the collected file

Note: The URL received from the trendmicro-visionone-download-information-for-collected-forensic-file will be valid for only 60 seconds

trendmicro-visionone-submit-file-to-sandbox


Submits a file to the sandbox for analysis (Note. For more information about the supported file types, see the TrendAI Vision One™ Online Help. Submissions require credits. Does not require credits in regions where Sandbox Analysis has not been officially released.)

Base Command

  1. trendmicro-visionone-submit-file-to-sandbox

Input

Argument Name Description Required
file_url URL pointing to the location of the file to be submitted. Required
filename Name of the file to be analyzed. Required
document_password The Base64 encoded password for decrypting the submitted document. sample. Optional
archive_password The Base64 encoded password for decrypting the submitted archive. Optional

Context Output

Path Type Description
VisionOne.Submit_File_to_Sandbox.message string Status message of the file submitted to sandbox.
VisionOne.Submit_File_to_Sandbox.code string status code of the file submitted to sandbox
VisionOne.Submit_File_to_Sandbox.task_id string Task ID of the submitted file
VisionOne.Submit_File_to_Sandbox.digest unknown The hash value of the file

trendmicro-visionone-submit-file-entry-to-sandbox


Submits the file corresponding to EntryID to the sandbox for analysis (Note. For more information about the supported file types, see the TrendAI Vision One™ One Online Help. Submissions require credits. Does not require credits in regions where Sandbox Analysis has not been officially released.)

Base Command

  1. trendmicro-visionone-submit-file-entry-to-sandbox

Input

Argument Name Description Required
entry_id Entry ID of the file to be submitted. Required
document_password The Base64 encoded password for decrypting the submitted document. sample. Optional
archive_password The Base64 encoded password for decrypting the submitted archive. Optional

Context Output

Path Type Description
VisionOne.Submit_File_Entry_to_Sandbox.message string Status message of the file submitted to sandbox.
VisionOne.Submit_File_Entry_to_Sandbox.code string status code of the file submitted to sandbox
VisionOne.Submit_File_Entry_to_Sandbox.task_id string Task ID of the submitted file
VisionOne.Submit_File_Entry_to_Sandbox.digest string The hash value of the file
VisionOne.Submit_File_Entry_to_Sandbox.filename string The name of the file submitted
VisionOne.Submit_File_Entry_to_Sandbox.file_path string The path to the file associated to incident
VisionOne.Submit_File_Entry_to_Sandbox.entryId string The Entry ID for the file

trendmicro-visionone-run-sandbox-submission-polling


Runs a polling command to retrieve the status of a sandbox analysis submission

Base Command

  1. trendmicro-visionone-run-sandbox-submission-polling

Input

Argument Name Description Required
polling polling the task for 30 seconds interval. Default is true. Optional
task_id task_id from the trendmicro-visionone-submit-file-to-sandbox or trendmicro-visionone-submit-file-entry-to-sandbox command output. Required

Context Output

Path Type Description
VisionOne.Sandbox_Submission_Polling.message string Status of the sandbox analysis
VisionOne.Sandbox_Submission_Polling.code string Response code
VisionOne.Sandbox_Submission_Polling.task_id string task_id of the task queried
VisionOne.Sandbox_Submission_Polling.taskStatus string Sandbox analysis status
VisionOne.Sandbox_Submission_Polling.digest string The hash values of file analyzed
VisionOne.Sandbox_Submission_Polling.analysis_completion_time string Sample analysis completed time.
VisionOne.Sandbox_Submission_Polling.risk_level string Risk Level of the analyzed file.
VisionOne.Sandbox_Submission_Polling.description string Scan result description for NotAnalyzed.
VisionOne.Sandbox_Submission_Polling.detection_name_list unknown Detection name of this sample, if applicable.
VisionOne.Sandbox_Submission_Polling.threat_type_list unknown Threat type of this sample.
VisionOne.Sandbox_Submission_Polling.file_type string File type of this sample.
VisionOne.Sandbox_Submission_Polling.report_id string ID used to get the report and suspicious object. Empty means no report.
VisionOne.Sandbox_Submission_Polling.DBotScore.score number The DBot score.
VisionOne.Sandbox_Submission_Polling.DBotScore.Vendor string The Vendor name.
VisionOne.Sandbox_Submission_Polling.DBotScore.Reliability string The reliability level.

trendmicro-visionone-check-task-status


Command gives the status of the running task based on the action id.

Base Command

  1. trendmicro-visionone-check-task-status

Input

Argument Name Description Required
polling polling the task for 30 seconds interval. Default is true. Optional
actionId Action id of the task you would like to check. Required

Context Output

Path Type Description
VisionOne.Task_Status.actionId unknown Action ID of the task queried.
VisionOne.Task_Status.taskStatus unknown Status of the task.

trendmicro-visionone-add-note


Attaches a note to a workbench alert

Base Command

  1. trendmicro-visionone-add-note

Input

Argument Name Description Required
workbench_id ID of the workbench you would like to attach the note to. Required
content Contents of the note to be attached. Required

Context Output

Path Type Description
VisionOne.Add_Note.Workbench_Id string The ID of the workbench that the note was added to.
VisionOne.Add_Note.Note_Id string The ID of the note that was added.
VisionOne.Add_Note.Response_Code string The response code from the command
VisionOne.Add_Note.Response_Msg string The response message from the command

trendmicro-visionone-update-status


Updates the status of a workbench alert

Base Command

  1. trendmicro-visionone-update-status

Input

Argument Name Description Required
workbench_id ID of the workbench you would like to update the status for. Required
status Status to assign to the workbench alert. Possible values are: new, in_progress, resolved_true_positive, resolved_false_positive. Required

Context Output

Path Type Description
VisionOne.Update_Status.Workbench_Id string The ID of the workbench that had the status updated.
VisionOne.Update_Status.Response_Code string The response code from the command
VisionOne.Update_Status.Response_Msg string The response message from the command

Configuration parameters

  • url — API URL (e.g. https://api.xdr.trendmicro.com) (required)
  • apikey — (required)
  • isFetch — Fetch incidents
  • incidentFetchInterval — Incidents Fetch Interval
  • incidentType — Incident type
  • first_fetch — Sync On First Run (days)
  • max_fetch — Max Incidents
  • proxy — Use system proxy settings
  • insecure — Trust any certificate (not secure)
  • integrationReliability — Source Reliability

Commands (22)

  • trendmicro-visionone-add-note

    Attaches a note to a workbench alert.

  • trendmicro-visionone-add-objects-to-exception-list

    Adds domains, file SHA-1 values, IP addresses, or URLs to the Exception List and prevents these objects from being added to the Suspicious Object List.

  • trendmicro-visionone-add-objects-to-suspicious-list

    Adds domains, file SHA-1 values, IP addresses, or URLs to the Suspicious Object List.

  • trendmicro-visionone-add-to-block-list

    Adds a file SHA-1, IP address, domain, or URL object to the User-Defined Suspicious Objects List, which blocks the objects on subsequent detections.

  • trendmicro-visionone-check-task-status

    Command gives the status of the running task based on the action id.

  • trendmicro-visionone-collect-forensic-file

    Compresses a file on an endpoint in a password-protected archive and then sends the archive to the XDR service platform.

  • trendmicro-visionone-delete-email-message

    Deletes a message from a mailbox.

  • trendmicro-visionone-delete-objects-from-exception-list

    Deletes domains, file SHA-1 values, IP addresses, or URLs from the Exception List.

  • trendmicro-visionone-delete-objects-from-suspicious-list

    Deletes domains, file SHA-1 values, IP addresses, or URLs from the Suspicious Object List.

  • trendmicro-visionone-download-information-for-collected-forensic-file

    Retrieves a URL and other information required to download a collected file via the trendmicro-visionone-collect-forensic-file command.

  • trendmicro-visionone-get-endpoint-info

    Retrieves information about a specific endpoint.

  • trendmicro-visionone-get-file-analysis-report

    Retrieves the analysis report, investigation package, or Suspicious Object List of a submitted file.

  • trendmicro-visionone-get-file-analysis-status

    Retrieves the status of a sandbox analysis submission.

  • trendmicro-visionone-isolate-endpoint

    Disconnects an endpoint from the network (but allows communication with the managing TrendAI™ product).

  • trendmicro-visionone-quarantine-email-message

    Moves a message from a mailbox to the quarantine folder.

  • trendmicro-visionone-remove-from-block-list

    Removes a file SHA-1, IP address, domain, or URL from the User-Defined Suspicious Objects List.

  • trendmicro-visionone-restore-endpoint-connection

    Restores network connectivity to an endpoint that applied the "isolate endpoint" action.

  • trendmicro-visionone-run-sandbox-submission-polling

    Runs a polling command to retrieve the status of a sandbox analysis submission.

  • trendmicro-visionone-submit-file-entry-to-sandbox

    Submits the file corresponding to EntryID to the sandbox for analysis (Note. For more information about the supported file types, see the TrendAI Vision One™ Online Help. Submissions require credits. Does not require credits in regions where Sandbox Analysis has not been officially released.)

  • trendmicro-visionone-submit-file-to-sandbox

    Submits a file to the sandbox for analysis (Note. For more information about the supported file types, see the TrendAI Vision One™ Online Help. Submissions require credits. Does not require credits in regions where Sandbox Analysis has not been officially released.)

  • trendmicro-visionone-terminate-process

    Terminates a process that is running on an endpoint.

  • trendmicro-visionone-update-status

    Updates the status of a workbench alert.

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

"""IMPORTS"""
import base64
import json
import re
from datetime import UTC, datetime, timedelta
from typing import Any

import requests
import urllib3
from requests.models import HTTPError

from CommonServerUserPython import *  # noqa: F401

"""CONSTANTS"""
USER_AGENT = "TMV1CortexXSOARApp/1.1"
VENDOR_NAME = "TrendMicroVisionOne"
URL = "url"
POST = "post"
GET = "get"
PUT = "put"
AUTHORIZATION = "Authorization"
BEARER = "Bearer "
CONTENT_TYPE_JSON = "application/json"
EMPTY_STRING = ""
ASCII = "ascii"
API_TOKEN = "apikey"
VALUE_TYPE = "value_type"
TARGET_VALUE = "target_value"
PRODUCT_ID = "product_id"
DESCRIPTION = "description"
MESSAGE_ID = "message_id"
MAILBOX = "mailbox"
MESSAGE_DELIVERY_TIME = "message_delivery_time"
COMPUTER_ID = "computer_id"
FIELD = "field"
ENDPOINT = "endpoint"
DATA = "data"
TYPE = "type"
VALUE = "value"
FILESHA = "file_sha1"
FILENAME = "filename"
CRITERIA = "criteria"
EXCEPTION_LIST = "exceptionList"
SUSPICIOUS_LIST = "suspiciousObjectList"
LAST_MODIFIED = "lastModified"
SCAN_ACTION = "scan_action"
RISK_LEVEL = "risk_level"
EXPIRYDAY = "expiry_days"
TASKID = "task_id"
REPORT_ID = "report_id"
ENTRY_ID = "entry_id"
TASKSTATUS = "taskStatus"
OS_TYPE = "os"
FILE_PATH = "file_path"
FILE_URL = "file_url"
FILE_NAME = "filename"
DOCUMENT_PASSWORD = "document_password"
ARCHIVE_PASSWORD = "archive_password"
ACTION_ID = "actionId"
WORKBENCH_ID = "workbench_id"
CONTENT = "content"
STATUS = "status"
# End Points
ADD_BLOCKLIST_ENDPOINT = "/v2.0/xdr/response/block"
REMOVE_BLOCKLIST_ENDPOINT = "/v2.0/xdr/response/restoreBlock"
QUARANTINE_EMAIL_ENDPOINT = "/v2.0/xdr/response/quarantineMessage"
DELETE_EMAIL_ENDPOINT = "/v2.0/xdr/response/deleteMessage"
ISOLATE_CONNECTION_ENDPOINT = "/v2.0/xdr/response/isolate"
TERMINATE_PROCESS_ENDPOINT = "/v2.0/xdr/response/terminateProcess"
RESTORE_CONNECTION_ENDPOINT = "/v2.0/xdr/response/restoreIsolate"
ADD_OBJECT_TO_EXCEPTION_LIST = "/v2.0/xdr/threatintel/suspiciousObjects/exceptions"
DELETE_OBJECT_FROM_EXCEPTION_LIST = "/v2.0/xdr/threatintel/suspiciousObjects/exceptions/delete"
ADD_OBJECT_TO_SUSPICIOUS_LIST = "/v2.0/xdr/threatintel/suspiciousObjects"
DELETE_OBJECT_FROM_SUSPICIOUS_LIST = "/v2.0/xdr/threatintel/suspiciousObjects/delete"
TASK_DETAIL_ENDPOINT = "/v2.0/xdr/response/getTask"
GET_COMPUTER_ID_ENDPOINT = "/v2.0/xdr/eiqs/query/agentInfo"
GET_ENDPOINT_INFO_ENDPOINT = "/v2.0/xdr/eiqs/query/endpointInfo"
GET_FILE_STATUS = "/v2.0/xdr/sandbox/tasks/{taskId}"
GET_FILE_REPORT = "/v2.0/xdr/sandbox/reports/{reportId}"
ADD_NOTE_ENDPOINT = "/v2.0/xdr/workbench/workbenches/{workbenchId}/notes"
UPDATE_STATUS_ENDPOINT = "/v2.0/xdr/workbench/workbenches/{workbenchId}"
COLLECT_FORENSIC_FILE = "/v2.0/xdr/response/collectFile"
DOWNLOAD_INFORMATION_COLLECTED_FILE = "/v2.0/xdr/response/downloadInfo"
SUBMIT_FILE_TO_SANDBOX = "/v2.0/xdr/sandbox/file"
WORKBENCH_HISTORIES = "/v2.0/xdr/workbench/workbenchHistories"
# Error Messages
RESPONSE_ERROR = "Error in API call: [%d] - %s"
RETRY_ERROR = "The max tries exceeded [%d] - %s"
COMMAND_CALLED = "Command being called is {command}"
COMMAND_EXECUTION_ERROR = "Failed to execute {error} command. Error"
AUTHORIZATION_ERROR = "Authorization Error: make sure URL/API Key is correctly set. Error - {error}"
PARAMETER_ISSUE = "{param} is not a valid parameter. Kindly provide valid parameter"
FILE_TYPE_ERROR = "Kindly provide valid file 'type'"
FILE_NOT_FOUND = "No such file present in {filepath}"
# General Messages:
RAW_RESPONSE = "The raw response data - {raw_response}"
SUCCESS_RESPONSE = "success with url {url} and response status {status}"
EXCEPTION_MESSAGE = "Successfully {task} object to exception list with response {code}, Total items in exception list - {length}"
SUCCESS_TEST = "Successfully connected to the vision one API."
POLLING_MESSAGE = "The task has not completed, will check status again in 30 seconds"
# Workbench Statuses
NEW = 0
IN_PROGRESS = 1
RESOLVED_TRUE_POSITIVE = 2
RESOLVED_FALSE_POSITIVE = 3
# Table Heading
TABLE_ADD_TO_BLOCKLIST = "Add to block list "
TABLE_REMOVE_FROM_BLOCKLIST = "Remove from block list "
TABLE_QUARANTINE_EMAIL_MESSAGE = "Quarantine email message "
TABLE_DELETE_EMAIL_MESSAGE = "Delete email message "
TABLE_ISOLATE_ENDPOINT_MESSAGE = "Isolate endpoint connection "
TABLE_RESTORE_ENDPOINT_MESSAGE = "Restore endpoint connection "
TABLE_TERMINATE_PROCESS = "Terminate process "
TABLE_ADD_EXCEPTION_LIST = "Add object to exception list "
TABLE_DELETE_EXCEPTION_LIST = "Delete object from exception list "
TABLE_ADD_SUSPICIOUS_LIST = "Add object to suspicious list "
TABLE_ENDPOINT_INFO = "Endpoint info "
TABLE_DELETE_SUSPICIOUS_LIST = "Delete object from suspicious list "
TABLE_GET_FILE_ANALYSIS_STATUS = "File analysis status "
TABLE_GET_FILE_ANALYSIS_REPORT = "File analysis report "
TABLE_COLLECT_FILE = "Collect forensic file "
TABLE_COLLECTED_FORENSIC_FILE_DOWNLOAD_INFORMATION = "The download information for collected forensic file "
TABLE_SUBMIT_FILE_TO_SANDBOX = "Submit file to sandbox "
TABLE_SUBMIT_FILE_ENTRY_TO_SANDBOX = "Submit file entry to sandbox "
TABLE_SANDBOX_SUBMISSION_POLLING = "Sandbox submission polling status "
TABLE_ADD_NOTE = "Add note to workbench alert "
TABLE_UPDATE_STATUS = "Update workbench alert status"
# COMMAND NAMES
ADD_BLOCKLIST_COMMAND = "trendmicro-visionone-add-to-block-list"
REMOVE_BLOCKLIST_COMMAND = "trendmicro-visionone-remove-from-block-list"
QUARANTINE_EMAIL_COMMAND = "trendmicro-visionone-quarantine-email-message"
DELETE_EMAIL_COMMAND = "trendmicro-visionone-delete-email-message"
ISOLATE_ENDPOINT_COMMAND = "trendmicro-visionone-isolate-endpoint"
RESTORE_ENDPOINT_COMMAND = "trendmicro-visionone-restore-endpoint-connection"
TERMINATE_PROCESS_COMMAND = "trendmicro-visionone-terminate-process"
ADD_EXCEPTION_LIST_COMMAND = "trendmicro-visionone-add-objects-to-exception-list"
DELETE_EXCEPTION_LIST_COMMAND = "trendmicro-visionone-delete-objects-from-exception-list"
ADD_SUSPICIOUS_LIST_COMMAND = "trendmicro-visionone-add-objects-to-suspicious-list"
DELETE_SUSPICIOUS_LIST_COMMAND = "trendmicro-visionone-delete-objects-from-suspicious-list"
GET_FILE_ANALYSIS_STATUS = "trendmicro-visionone-get-file-analysis-status"
GET_FILE_ANALYSIS_REPORT = "trendmicro-visionone-get-file-analysis-report"
COLLECT_FILE = "trendmicro-visionone-collect-forensic-file"
DOWNLOAD_COLLECTED_FILE = "trendmicro-visionone-download-information-for-collected-forensic-file"
FILE_TO_SANDBOX = "trendmicro-visionone-submit-file-to-sandbox"
FILE_ENTRY_TO_SANDBOX = "trendmicro-visionone-submit-file-entry-to-sandbox"
SANDBOX_SUBMISSION_POLLING = "trendmicro-visionone-run-sandbox-submission-polling"
CHECK_TASK_STATUS = "trendmicro-visionone-check-task-status"
GET_ENDPOINT_INFO_COMMAND = "trendmicro-visionone-get-endpoint-info"
UPDATE_STATUS = "trendmicro-visionone-update-status"
ADD_NOTE = "trendmicro-visionone-add-note"
FETCH_INCIDENTS = "fetch-incidents"

table_name = {
    ADD_BLOCKLIST_COMMAND: TABLE_ADD_TO_BLOCKLIST,
    REMOVE_BLOCKLIST_COMMAND: TABLE_REMOVE_FROM_BLOCKLIST,
    QUARANTINE_EMAIL_COMMAND: TABLE_QUARANTINE_EMAIL_MESSAGE,
    DELETE_EMAIL_COMMAND: TABLE_DELETE_EMAIL_MESSAGE,
    ISOLATE_ENDPOINT_COMMAND: TABLE_ISOLATE_ENDPOINT_MESSAGE,
    RESTORE_ENDPOINT_COMMAND: TABLE_RESTORE_ENDPOINT_MESSAGE,
    ADD_EXCEPTION_LIST_COMMAND: TABLE_ADD_EXCEPTION_LIST,
    DELETE_EXCEPTION_LIST_COMMAND: TABLE_DELETE_EXCEPTION_LIST,
    ADD_SUSPICIOUS_LIST_COMMAND: TABLE_ADD_SUSPICIOUS_LIST,
    GET_ENDPOINT_INFO_COMMAND: TABLE_ENDPOINT_INFO,
    DELETE_SUSPICIOUS_LIST_COMMAND: TABLE_DELETE_SUSPICIOUS_LIST,
}
# disable insecure warnings
urllib3.disable_warnings()


def check_datetime_aware(d):
    return (d.tzinfo is not None) and (d.tzinfo.utcoffset(d) is not None)


class Client(BaseClient):
    def __init__(self, base_url: str, api_key: str, proxy: bool, verify: bool) -> None:
        """
        Inherit the BaseClient class from the demistomock.
        :type base_url: ``str``
        :param base_url: Base server address with suffix, for example: https://example.com/api/v2/.
        :type api_key: ``str``
        :param api_key: api token to access the api data.
        :type proxy: ``bool``
        :param proxy: Whether the request should use the system proxy settings.
        :type verify: ``bool``
        :param verify: Whether the request should verify the SSL certificate.
        :return: returns None
        :rtype: ``None``
        """
        self.base_url = base_url
        self.api_key = api_key
        self.status = None

        super().__init__(base_url=base_url, proxy=proxy, verify=verify)

    def http_request(self, method: str, url_suffix: str, json_data=None, params=None, data=None) -> Any:
        """
        Override http_request method from BaseClient class. This method will print an error based on status code
        and exceptions.
        :type method: ``str``
        :param method: The HTTP method, for example: GET, POST, and so on.
        :type url_suffix: ``str``
        :param url_suffix: The API endpoint.
        :type json_data: ``dict``
        :param json_data: The dictionary to send in a 'POST' request.
        :type params: ``dict``
        :param params: URL parameters to specify the query.
        :type data: ``dict``
        :param data: The data to send in a 'POST' request.
        :return: response data
        :rtype: ``dict`` or ``str`` or ``requests.Response``
        """
        header = {
            "Authorization": f"Bearer {self.api_key}",
            "Content-Type": f"{CONTENT_TYPE_JSON};charset=utf-8",
            "User-Agent": USER_AGENT,
        }
        try:
            response = self._http_request(
                method=method,
                full_url=f"{self.base_url}{url_suffix}",
                retries=3,
                json_data=json_data,
                params=params,
                headers=header,
                resp_type="response",
                ok_codes=(200, 201),
                data=data,
            )
        except DemistoException as error:
            demisto.error(error.message)
            return_error(error.message)
        if response.ok:
            demisto.info(SUCCESS_RESPONSE.format(url=f"{self.base_url}{url_suffix}", status=response.status_code))
            self.status = response.status_code
            content_type = response.headers.get("Content-Type", "")
            if content_type.__contains__(CONTENT_TYPE_JSON):
                # Handle empty response
                if response.text == EMPTY_STRING:
                    return response
                else:
                    return response.json()
            else:
                return response
        return None

    def status_check(self, data: dict[str, Any]) -> Any:
        """
        Check the status of particular task.
        :type data: ``dict``
        :param method: Response data to received from the end point.
        :return: task status response data.
        :rtype: ``Any``
        """
        action_id = data.get(ACTION_ID)
        params = {"actionId": action_id}
        response = self.http_request(GET, TASK_DETAIL_ENDPOINT, params=params)
        message = {
            "actionId": action_id,
            "taskStatus": response.get("data").get("taskStatus"),
        }
        return CommandResults(
            readable_output=tableToMarkdown("Status of task ", message, removeNull=True),
            outputs_prefix=("VisionOne.Task_Status"),
            outputs_key_field="actionId",
            outputs=message,
        )

    def sandbox_submission_polling(self, data: dict[str, Any]) -> Any:
        """
        Check the status of sandbox submission
        :type data: ``dict``
        :param method: Response data received from sandbox.
        :return: Sandbox submission response data.
        :rtype: ``Any``
        """
        task_id = data.get(TASKID)
        result = self.http_request(GET, GET_FILE_STATUS.format(taskId=task_id))
        risk = result.get("data", {}).get("analysisSummary", {}).get("riskLevel", "")
        risk_score = self.incident_severity_to_dbot_score(risk)
        sha256 = result.get("data", {}).get("digest", {}).get("sha256")
        md5 = result.get("data", {}).get("digest", {}).get("md5")
        sha1 = result.get("data", {}).get("digest", {}).get("sha1")
        reliability = demisto.params().get("integrationReliability")
        dbot_score = Common.DBotScore(
            indicator=sha256,
            indicator_type=DBotScoreType.FILE,
            integration_name=VENDOR_NAME,
            score=risk_score,
            reliability=reliability,
        )
        file_entry = Common.File(sha256=sha256, md5=md5, sha1=sha1, dbot_score=dbot_score)
        message = {
            "message": result.get("message", ""),
            "code": result.get("code", ""),
            "task_id": result.get("data", {}).get("taskId", ""),
            "taskStatus": result.get("data", {}).get("taskStatus", ""),
            "digest": result.get("data", {}).get("digest", ""),
            "analysis_completion_time": result.get("data", {}).get("analysisSummary", "").get("analysisCompletionTime", ""),
            "risk_level": result.get("data", {}).get("analysisSummary", "").get("riskLevel", ""),
            "description": result.get("data", {}).get("analysisSummary", "").get("description", ""),
            "detection_name_list": result.get("data", {}).get("analysisSummary", "").get("detectionNameList", ""),
            "threat_type_list": result.get("data", {}).get("analysisSummary", "").get("threatTypeList", ""),
            "file_type": result.get("data", {}).get("analysisSummary", "").get("trueFileType", ""),
            "report_id": result.get("data", {}).get("reportId", ""),
            "DBotScore": {
                "Score": dbot_score.score,
                "Vendor": dbot_score.integration_name,
                "Reliability": dbot_score.reliability,
            },
        }
        return CommandResults(
            readable_output=tableToMarkdown(TABLE_SANDBOX_SUBMISSION_POLLING, message, removeNull=True),
            outputs_prefix="VisionOne.Sandbox_Submission_Polling",
            outputs_key_field="report_id",
            outputs=message,
            indicator=file_entry,
        )

    def lookup_type(self, param: Any) -> str:
        # Regex expression for validating IPv4
        regex = (
            "(([0-9]|[1-9][0-9]|1[0-9][0-9]|"
            "2[0-4][0-9]|25[0-5])\\.){3}"
            "([0-9]|[1-9][0-9]|1[0-9][0-9]|"
            "2[0-4][0-9]|25[0-5])"
        )

        # Regex expression for validating IPv6
        regex1 = "((([0-9a-fA-F]){1,4})\\:){7}" + "([0-9a-fA-F]){1,4}"

        # Regex expression for validating mac
        regex2 = "([0-9A-Fa-f]{2}[:-]){5}" + "([0-9A-Fa-f]{2})"

        p = re.compile(regex)
        p1 = re.compile(regex1)
        p2 = re.compile(regex2)

        # Checking if it is a valid IPv4 addresses
        if re.search(p, param):
            return "ip"

        # Checking if it is a valid IPv6 addresses
        elif re.search(p1, param):
            return "ipv6"

        # Checking if it is a valid IPv6 addresses
        elif re.search(p2, param):
            return "macaddr"

        # Otherwise use hostname type
        return "hostname"

    def get_computer_id(self, field: Any, value: Any) -> str:
        """
        Fetch particular computer id using hostname, macaddress or ip.
        :type field: ``str``
        :param field: type of field to search hostname, macaddress or ip.
        :type value: ``str``
        :param value: value of the particular field.
        :return: value of computer id.
        :rtype: ``str``
        """
        body = {CRITERIA: {FIELD: field, VALUE: value}}
        response = self.http_request(POST, GET_COMPUTER_ID_ENDPOINT, data=json.dumps(body))

        if response["status"] == "FAIL":
            return_error("kindly provide valid field value")
        computer_id = response.get("result").get("computerId")
        return computer_id

    def exception_list_count(self) -> int:
        """
        Gets the count of object present in exception list

        :return: number of exception object.
        :rtype: ``int``
        """
        response = self.http_request(GET, ADD_OBJECT_TO_EXCEPTION_LIST)
        list_of_exception = response.get(DATA).get(EXCEPTION_LIST)
        exception_count = len(list_of_exception)
        return exception_count

    def suspicious_list_count(self) -> int:
        """
        Gets the count of object present in suspicious list
        :return: number of suspicious object.
        :rtype: ``int``
        """
        response = self.http_request(GET, ADD_OBJECT_TO_SUSPICIOUS_LIST)
        list_of_exception = response.get(DATA).get(SUSPICIOUS_LIST)
        exception_count = len(list_of_exception)
        return exception_count

    def get_workbench_histories(self, start, end, offset=None, size=None) -> str:
        if not check_datetime_aware(start):
            start = start.astimezone()
        if not check_datetime_aware(end):
            end = end.astimezone()
        start = start.astimezone(UTC)
        end = end.astimezone(UTC)
        start = start.isoformat(timespec="milliseconds").replace("+00:00", "Z")
        end = end.isoformat(timespec="milliseconds").replace("+00:00", "Z")

        params = dict(
            [("startTime", start), ("endTime", end), ("sortBy", "createdTime")]
            + ([("offset", offset)] if offset is not None else [])
            + ([("limit", size)] if size is not None else [])
        )

        response = self.http_request(GET, WORKBENCH_HISTORIES, params=params)["data"]["workbenchRecords"]
        return response

    def incident_severity_to_dbot_score(self, severity: str):
        """
        Converts an priority string to DBot score representation
            alert severity. Can be one of:
            Unknown -> 0
            No Risk -> 1
            Low or Medium -> 2
            Critical or High -> 3
        Args:
            severity: String representation of severity.
        Returns:
            Dbot representation of severity
        """
        if not isinstance(severity, str):
            return 0

        if severity == "noRisk":
            return 1
        if severity in ["low", "medium"]:
            return 2
        if severity in ["high", "critical"]:
            return 3
        return 0


def run_polling_command(args: dict[str, Any], cmd: str, client: Client) -> str | CommandResults:
    """
    Performs polling interval to check status of task or sandbox submission result.
    :type args: ``args``
    :param client: argument required for polling.

    :type client: ``cmd``
    :param client: The command that polled for an interval.

    :type client: ``Client``
    :param client: client object to use http_request.
    """
    ScheduledCommand.raise_error_if_not_supported()
    interval_in_secs = int(args.get("interval_in_seconds", 30))
    action_id = args.get(ACTION_ID)
    task_id = args.get(TASKID)
    if cmd == CHECK_TASK_STATUS:
        command_results = client.status_check(args)
        value = ACTION_ID
    else:
        command_results = client.sandbox_submission_polling(args)
        value = TASKID
    if command_results.outputs.get("taskStatus") not in (
        "success",
        "failed",
        "timeout",
        "skipped",
        "finished",
    ):
        # schedule next poll
        polling_args = {
            f"{value}": action_id if action_id else task_id,
            "interval_in_seconds": interval_in_secs,
            "polling": True,
            **args,
        }
        scheduled_command = ScheduledCommand(
            command=cmd,
            next_run_in_seconds=interval_in_secs,
            args=polling_args,
            timeout_in_seconds=1500,
        )  # The timeout interval set for 25 minutes.
        command_results = CommandResults(scheduled_command=scheduled_command)
    return command_results


def get_task_status(args: dict[str, Any], client: Client) -> str | CommandResults:
    """
    check status of task.

    :type args: ``args``
    :param client: argument required for polling.

    :type client: ``Client``
    :param client: client object to use http_request.
    """
    return run_polling_command(args, CHECK_TASK_STATUS, client)


def get_sandbox_submission_status(args: dict[str, Any], client: Client) -> str | CommandResults:
    """
    call polling command to check status of sandbox submission.

    :type args: ``args``
    :param client: argument required for polling.

    :type client: ``Client``
    :param client: client object to use http_request.
    """
    return run_polling_command(args, SANDBOX_SUBMISSION_POLLING, client)


def test_module(client: Client) -> Any:
    """
    Performs basic get request to get item samples.
    :type client: ``Client``
    :param client: client object to use http_request.
    """
    client.http_request("GET", "/v2.0/xdr/threatintel/suspiciousObjects/exceptions")
    return "ok"


def get_endpoint_info(client: Client, args: dict[str, Any]) -> str | CommandResults:
    """
    Retrieve information abouut the endpoint queried and
    sends the result to demisto war room.

    :type client: ``Client``
    :param client: client object to use http_request.

    :type args: ``dict``
    :param args: args object to fetch the argument data.

    :return: sends data to demisto war room.
    :rtype: ``dict`
    """

    value = args.get(ENDPOINT)
    field = client.lookup_type(value)

    computer_id = client.get_computer_id(field, value)
    body = {"computerId": computer_id}
    response = client.http_request(POST, GET_ENDPOINT_INFO_ENDPOINT, data=json.dumps(body))

    message = {
        "message": response.get("message", ""),
        "errorCode": response.get("errorCodecode", ""),
        "status": response.get("status", ""),
        "logonAccount": response.get("result", {}).get("logonAccount", "").get("value", ""),
        "hostname": response.get("result", {}).get("hostname", "").get("value", ""),
        "macAddr": response.get("result", {}).get("macAddr", "").get("value", ""),
        "ip": response.get("result", {}).get("ip", "").get("value", ""),
        "osName": response.get("result", {}).get("osName", ""),
        "osVersion": response.get("result", {}).get("osVersion", ""),
        "osDescription": response.get("result", {}).get("osDescription", ""),
        "productCode": response.get("result", {}).get("productCode", ""),
    }

    results = CommandResults(
        readable_output=tableToMarkdown(table_name[GET_ENDPOINT_INFO_COMMAND], message, removeNull=True),
        outputs_prefix="VisionOne.Endpoint_Info",
        outputs_key_field="message",
        outputs=message,
    )
    return results


def add_delete_block_list_mapping(data: dict[str, Any]) -> dict[str, Any]:
    """
    Mapping add to block list response data.

    :type data: ``dict``
    :param data: Response data to received from the end point.

    :return: mapped response data.
    :rtype: ``dict``
    """
    action_id = data.get("actionId", {})
    task_status = data.get("taskStatus", {})
    return {"actionId": action_id, "taskStatus": task_status}


def add_or_remove_from_block_list(client: Client, command: str, args: dict[str, Any]) -> str | CommandResults:
    """
    Retrieve data from the add or remove from block list and
    sends the result to demist war room.

    :type client: ``Client``
    :param client: client object to use http_request.

    :type command: ``str``
    :param command: type of command either
    trendmicro-visionone-add-to-block-list or
    trendmicro-visionone-remove-from-block-list.

    :type args: ``dict``
    :param args: args object to fetch the argument data.

    :return: sends data to demisto war room.
    :rtype: ``dict`
    """
    value_type = args.get(VALUE_TYPE)
    target_value = args.get(TARGET_VALUE)
    product_id = args.get(PRODUCT_ID)
    if not product_id:
        product_id = EMPTY_STRING
    description = args.get(DESCRIPTION)
    if not description:
        description = EMPTY_STRING
    body = {
        "valueType": value_type,
        "targetValue": target_value,
        "productId": product_id,
        "description": description,
    }
    if command == ADD_BLOCKLIST_COMMAND:
        response = client.http_request(POST, ADD_BLOCKLIST_ENDPOINT, data=json.dumps(body))
    elif command == REMOVE_BLOCKLIST_COMMAND:
        response = client.http_request(POST, REMOVE_BLOCKLIST_ENDPOINT, data=json.dumps(body))
    else:
        response = None
        demisto.debug(f"{command} didn't mach any condition. {response=}")

    mapping_data = add_delete_block_list_mapping(response)
    results = CommandResults(
        readable_output=tableToMarkdown(table_name[command], mapping_data, removeNull=True),
        outputs_prefix="VisionOne.BlockList",
        outputs_key_field="actionId",
        outputs=mapping_data,
    )
    return results


def fetch_incidents(client: Client):
    """
    This function do the loop to get all workbench alerts by changing
    the parameters of both 'offset' and 'size'.
    """
    offset = 0
    size = demisto.params().get("max_fetch")
    end = datetime.now(UTC)
    days = int(demisto.params().get("first_fetch"))

    last_run = demisto.getLastRun()
    if last_run and "start_time" in last_run:
        start = datetime.fromisoformat(last_run.get("start_time"))
    else:
        start = end + timedelta(days=-days)

    alerts: List[Any] = []
    alerts.extend(client.get_workbench_histories(start, end, offset, size))

    incidents = []
    if alerts:
        for record in alerts:
            incident = {
                "name": record["workbenchName"],
                "occurred": record["createdTime"],
                "severity": client.incident_severity_to_dbot_score(record["severity"]),
                "rawJSON": json.dumps(record),
            }
            incidents.append(incident)
            last_event = datetime.strptime(record["createdTime"], "%Y-%m-%dT%H:%M:%SZ")

        next_search = last_event + timedelta(0, 1)

        demisto.setLastRun({"start_time": next_search.isoformat()})

    if incidents:
        demisto.incidents(incidents)
    else:
        demisto.incidents([])

    return incidents


def quarantine_delete_email_mapping(data: dict[str, Any]) -> dict[str, Any]:
    """
    Mapping quarantine email message response data.

    :type data: ``dict``
    :param method: Response data to received from the end point.

    :return: mapped response data.
    :rtype: ``dict``
    """
    action_id = data.get("actionId", {})
    task_status = data.get("taskStatus", {})
    return {"actionId": action_id, "taskStatus": task_status}


def quarantine_or_delete_email_message(client: Client, command: str, args: dict[str, Any]) -> str | CommandResults:
    """
    Retrieve data from the quarantine or delete email message and
    sends the result to demist war room.

    :type client: ``Client``
    :param client: client object to use http_request.

    :type command: ``str``
    :param command: type of command either
    trendmicro-visionone-quarantine-email-message or
    trendmicro-visionone-delete-email-message

    :type args: ``dict``
    :param args: args object to fetch the argument data.

    :return: sends data to demisto war room.
    :rtype: ``dict`
    """
    message_id = args.get(MESSAGE_ID)
    mailbox = args.get(MAILBOX)
    message_delivery_time = args.get(MESSAGE_DELIVERY_TIME)
    product_id = args.get(PRODUCT_ID)
    description = args.get(DESCRIPTION)
    if not description:
        description = EMPTY_STRING
    body = {
        "messageId": message_id,
        "mailBox": mailbox,
        "messageDeliveryTime": message_delivery_time,
        "productId": product_id,
        "description": description,
    }
    if command == QUARANTINE_EMAIL_COMMAND:
        response = client.http_request(POST, QUARANTINE_EMAIL_ENDPOINT, data=json.dumps(body))

    elif command == DELETE_EMAIL_COMMAND:
        response = client.http_request(POST, DELETE_EMAIL_ENDPOINT, data=json.dumps(body))
    else:
        response = None
        demisto.debug(f"{command=} didn't match any condition. {response=}")

    mapping_data = quarantine_delete_email_mapping(response)
    results = CommandResults(
        readable_output=tableToMarkdown(table_name[command], mapping_data, removeNull=True),
        outputs_prefix="VisionOne.Email",
        outputs_key_field="actionId",
        outputs=mapping_data,
    )
    return results


def isolate_restore_endpoint_mapping(data: dict[str, Any]) -> dict[str, Any]:
    """
    Mapping isolate endpoint and restore endpoint response data.

    :type data: ``dict``
    :param method: Response data to received from the end point.

    :return: mapped response data.
    :rtype: ``dict``
    """
    action_id = data.get("actionId", {})
    task_status = data.get("taskStatus", {})
    return {"actionId": action_id, "taskStatus": task_status}


def isolate_or_restore_connection(client: Client, command: str, args: dict[str, Any]) -> str | CommandResults:
    """
    Retrieve data from the isolate or restore endpoint connection and
    sends the result to demist war room.

    :type client: ``Client``
    :param client: client object to use http_request.

    :type command: ``str``
    :param command: type of command either
    trendmicro-visionone-isolate-endpoint or
    trendmicro-visionone-restore-endpoint-connection

    :type args: ``dict``
    :param args: args object to fetch the argument data.

    :return: sends data to demisto war room.
    :rtype: ``dict`
    """
    value = args.get(ENDPOINT)
    field = client.lookup_type(value)
    product_id = args.get(PRODUCT_ID)
    description = args.get(DESCRIPTION)
    if not description:
        description = EMPTY_STRING
    computer_id = client.get_computer_id(field, value)
    body = {
        "computerId": computer_id,
        "productId": product_id,
        "description": description,
    }
    if command == ISOLATE_ENDPOINT_COMMAND:
        response = client.http_request(POST, ISOLATE_CONNECTION_ENDPOINT, data=json.dumps(body))

    elif command == RESTORE_ENDPOINT_COMMAND:
        response = client.http_request(POST, RESTORE_CONNECTION_ENDPOINT, data=json.dumps(body))
    else:
        response = {}
        demisto.debug(f"The {command=} didn't match the conditions. {response=}")

    mapping_data = isolate_restore_endpoint_mapping(response)

    results = CommandResults(
        readable_output=tableToMarkdown(table_name[command], mapping_data, removeNull=True),
        outputs_prefix="VisionOne.Endpoint_Connection",
        outputs_key_field="actionId",
        outputs=mapping_data,
    )
    return results


def terminate_process(client: Client, args: dict[str, Any]) -> str | CommandResults:
    """
    Terminate the process running on the end point and
    sends the result to demist war room.

    :type client: ``Client``
    :param client: client object to use http_request.

    :type args: ``dict``
    :param args: args object to fetch the argument data.

    :return: sends data to demisto war room.
    :rtype: ``dict`
    """
    file_list = []
    value = args.get(ENDPOINT)
    field = client.lookup_type(value)
    product_id = args.get(PRODUCT_ID)
    description = args.get(DESCRIPTION)
    if not description:
        description = EMPTY_STRING
    computer_id = client.get_computer_id(field, value)
    file_sha1 = args.get(FILESHA)
    filename = args.get(FILENAME)
    if filename:
        file_list.append(filename)
    body = {
        "computerId": computer_id,
        "fileSha1": file_sha1,
        "productId": product_id,
        "description": description,
        "filename": file_list,
    }
    response = client.http_request(POST, TERMINATE_PROCESS_ENDPOINT, data=json.dumps(body))

    action_id = response.get("actionId", {})
    task_status = response.get("taskStatus", {})
    message = {"actionId": action_id, "taskStatus": task_status}
    results = CommandResults(
        readable_output=tableToMarkdown(TABLE_TERMINATE_PROCESS, message, removeNull=True),
        outputs_prefix="VisionOne.Terminate_Process",
        outputs_key_field="actionId",
        outputs=message,
    )
    return results


def add_or_delete_from_exception_list(client: Client, command: str, args: dict[str, Any]) -> str | CommandResults:
    """
    Add or Delete the exception object to exception list and
    sends the result to demist war room.

    :type client: ``Client``
    :param client: client object to use http_request.

    :type command: ``str``
    :param command: type of command either
    trendmicro-visionone-add-objects-to-exception-list or
    trendmicro-visionone-delete-objects-from-exception-list

    :type args: ``dict``
    :param args: args object to fetch the argument data.

    :return: sends data to demisto war room.
    :rtype: ``dict`
    """
    types = args.get(TYPE)
    value = args.get(VALUE)
    body = {DATA: [{"type": types, "value": value}]}
    if command == ADD_EXCEPTION_LIST_COMMAND:
        description = args.get(DESCRIPTION)
        if not description:
            description = EMPTY_STRING
        body[DATA][0][DESCRIPTION] = description
        client.http_request(POST, ADD_OBJECT_TO_EXCEPTION_LIST, data=json.dumps(body))

    elif command == DELETE_EXCEPTION_LIST_COMMAND:
        client.http_request(POST, DELETE_OBJECT_FROM_EXCEPTION_LIST, data=json.dumps(body))

    exception_list = client.exception_list_count()

    message = {
        "message": "success",
        "status_code": client.status,
        "total_items": exception_list,
    }
    results = CommandResults(
        readable_output=tableToMarkdown(table_name[command], message, removeNull=True),
        outputs_prefix="VisionOne.Exception_List",
        outputs_key_field="message",
        outputs=message,
    )
    return results


def add_to_suspicious_list(client: Client, args: dict[str, Any]) -> str | CommandResults:
    """
    Add suspicious object to suspicious list and
    sends the result to demist war room.

    :type client: ``Client``
    :param client: client object to use http_request.

    :type args: ``dict``
    :param args: args object to fetch the argument data.

    :return: sends data to demisto war room.
    :rtype: ``dict`
    """
    types = args.get(TYPE)
    value = args.get(VALUE)
    description = args.get(DESCRIPTION)
    if not description:
        description = EMPTY_STRING
    scan_action = args.get(SCAN_ACTION)
    if scan_action and scan_action not in ("log", "block"):
        return_error(PARAMETER_ISSUE.format(param=SCAN_ACTION))
    risk_level = args.get(RISK_LEVEL)
    if risk_level and risk_level not in ("high", "medium", "low"):
        return_error(PARAMETER_ISSUE.format(param=RISK_LEVEL))
    expiry = args.get(EXPIRYDAY)
    if not expiry:
        expiry = 0
    body = {
        DATA: [
            {
                "type": types,
                "value": value,
                "description": description,
                "scanAction": scan_action,
                "riskLevel": risk_level,
                "expiredDay": expiry,
            }
        ]
    }
    client.http_request(POST, ADD_OBJECT_TO_SUSPICIOUS_LIST, data=json.dumps(body))
    suspicious_list = client.suspicious_list_count()

    message = {
        "message": "success",
        "status_code": client.status,
        "total_items": suspicious_list,
    }
    results = CommandResults(
        readable_output=tableToMarkdown(table_name[ADD_SUSPICIOUS_LIST_COMMAND], message, removeNull=True),
        outputs_prefix="VisionOne.Suspicious_List",
        outputs_key_field="message",
        outputs=message,
    )
    return results


def delete_from_suspicious_list(client: Client, args: dict[str, Any]) -> str | CommandResults:
    """
    Delete the suspicious object from suspicious list and
    sends the result to demist war room.

    :type client: ``Client``
    :param client: client object to use http_request.

    :type args: ``dict``
    :param args: args object to fetch the argument data.

    :return: sends data to demisto war room.
    :rtype: ``dict`
    """
    types = args.get(TYPE)
    value = args.get(VALUE)
    body = {DATA: [{"type": types, "value": value}]}
    client.http_request(POST, DELETE_OBJECT_FROM_SUSPICIOUS_LIST, data=json.dumps(body))

    exception_list = client.suspicious_list_count()

    message = {
        "message": "success",
        "status_code": client.status,
        "total_items": exception_list,
    }
    results = CommandResults(
        readable_output=tableToMarkdown(table_name[DELETE_SUSPICIOUS_LIST_COMMAND], message, removeNull=True),
        outputs_prefix="VisionOne.Suspicious_List",
        outputs_key_field="message",
        outputs=message,
    )
    return results


def get_file_analysis_status(client: Client, args: dict[str, Any]) -> str | CommandResults:
    """
    Get the status of file based on task id and
    sends the result to demist war room

    :type client: ``Client``
    :param client: client object to use http_request.

    :type args: ``dict``
    :param args: args object to fetch the argument data.

    :return: sends data to demisto war room.
    :rtype: ``dict`
    """
    task_id = args.get(TASKID)
    response = client.http_request(GET, GET_FILE_STATUS.format(taskId=task_id))
    risk = response.get("data", {}).get("analysisSummary", {}).get("riskLevel", "")
    risk_score = client.incident_severity_to_dbot_score(risk)
    sha256 = response.get("data", {}).get("digest", {}).get("sha256")
    md5 = response.get("data", {}).get("digest", {}).get("md5")
    sha1 = response.get("data", {}).get("digest", {}).get("sha1")
    reliability = demisto.params().get("integrationReliability")
    dbot_score = Common.DBotScore(
        indicator=sha256,
        indicator_type=DBotScoreType.FILE,
        integration_name=VENDOR_NAME,
        score=risk_score,
        reliability=reliability,
    )

    file_entry = Common.File(sha256=sha256, md5=md5, sha1=sha1, dbot_score=dbot_score)

    message = {
        "message": response.get("message", ""),
        "code": response.get("code", ""),
        "task_id": response.get("data", {}).get("taskId", ""),
        "taskStatus": response.get("data", {}).get("taskStatus", ""),
        "digest": response.get("data", {}).get("digest", ""),
        "analysis_completion_time": response.get("data", {}).get("analysisSummary", "").get("analysisCompletionTime", ""),
        "risk_level": response.get("data", {}).get("analysisSummary", "").get("riskLevel", ""),
        "description": response.get("data", {}).get("analysisSummary", "").get("description", ""),
        "detection_name_list": response.get("data", {}).get("analysisSummary", "").get("detectionNameList", ""),
        "threat_type_list": response.get("data", {}).get("analysisSummary", "").get("threatTypeList", ""),
        "file_type": response.get("data", {}).get("analysisSummary", "").get("trueFileType", ""),
        "report_id": response.get("data", {}).get("reportId", ""),
        "DBotScore": {
            "Score": dbot_score.score,
            "Vendor": dbot_score.integration_name,
            "Reliability": dbot_score.reliability,
        },
    }
    results = CommandResults(
        readable_output=tableToMarkdown(TABLE_GET_FILE_ANALYSIS_STATUS, message, removeNull=True),
        outputs_prefix="VisionOne.File_Analysis_Status",
        outputs_key_field="message",
        outputs=message,
        indicator=file_entry,
    )
    return results


def get_file_analysis_report(client: Client, args: dict[str, Any]) -> str | CommandResults:
    """
    Get the report of file based on report id and sends the result to demist war room
    :type client: ``Client``
    :param client: client object to use http_request.
    :type args: ``dict``
    :param args: args object to fetch the argument data.
    :return: sends data to demisto war room.
    :rtype: ``dict`
    """
    report_id = args.get(REPORT_ID)
    types = args.get(TYPE)
    if types not in ("vaReport", "investigationPackage", "suspiciousObject"):
        return_error(FILE_TYPE_ERROR)
    params = {TYPE: types}
    response = client.http_request(GET, GET_FILE_REPORT.format(reportId=report_id), params=params)
    if isinstance(response, dict):
        message = {
            "message": response.get("message", ""),
            "code": response.get("code", ""),
            "data": [],
        }
        if len(response.get("data", [])) > 0:
            for data in response.get("data", {}):
                data_value = {
                    "type": data.get("type", ""),
                    "value": data.get("value", ""),
                    "risk_level": data.get("riskLevel", ""),
                    "analysis_completion_time": data.get("analysisCompletionTime", ""),
                    "expired_time": data.get("expiredTime", ""),
                    "root_file_sha1": data.get("rootFileSha1", ""),
                }
                message.get("data", {}).append(data_value)
        results = CommandResults(
            readable_output=tableToMarkdown(TABLE_GET_FILE_ANALYSIS_REPORT, message, removeNull=True),
            outputs_prefix="VisionOne.File_Analysis_Report",
            outputs_key_field="message",
            outputs=message,
        )
    elif response.headers.get("Content-Type", "") == "binary/octet-stream":
        data = response.content
        if types == "vaReport":
            results = fileResult("Sandbox_Analysis_Report.pdf", data, file_type=EntryType.ENTRY_INFO_FILE)
        else:
            results = fileResult(
                "Sandbox_Investigation_Package.zip",
                data,
                file_type=EntryType.ENTRY_INFO_FILE,
            )
    else:
        results = CommandResults()
        demisto.debug(f"The code didn't match any condition. {results=}")
    return results


def collect_file(client: Client, args: dict[str, Any]) -> str | CommandResults:
    """
    Collect forensic file and sends the result to demist war room
    :type client: ``Client``
    :param client: client object to use http_request.
    :type args: ``dict``
    :param args: args object to fetch the argument data.
    :return: sends data to demisto war room.
    :rtype: ``dict`
    """
    value = args.get(ENDPOINT)
    field = client.lookup_type(value)
    product_id = args.get(PRODUCT_ID)
    description = args.get(DESCRIPTION)
    if not description:
        description = EMPTY_STRING
    computer_id = client.get_computer_id(field, value)  # type: ignore
    file_path = args.get(FILE_PATH)
    os = args.get(OS_TYPE)
    body = {
        "description": description,
        "productId": product_id,
        "computerId": computer_id,
        "filePath": file_path,
        "os": os,
    }
    response = client.http_request(POST, COLLECT_FORENSIC_FILE, data=json.dumps(body))

    task_status = response.get("taskStatus", {})
    action_id = response.get("actionId", {})
    message = {"actionId": action_id, "taskStatus": task_status}
    results = CommandResults(
        readable_output=tableToMarkdown(TABLE_COLLECT_FILE, message, removeNull=True),
        outputs_prefix="VisionOne.Collect_Forensic_File",
        outputs_key_field="actionId",
        outputs=message,
    )
    return results


def download_information_collected_file(client: Client, args: dict[str, Any]) -> str | CommandResults:
    """
    Gets the download information for collected forensic file and sends the result to demist war room
    :type client: ``Client``
    :param client: client object to use http_request.
    :type args: ``dict``
    :param args: args object to fetch the argument data.
    :return: sends data to demisto war room.
    :rtype: ``dict`
    """
    action_id = args.get(ACTION_ID)
    params = {"actionId": action_id}
    response = client.http_request(GET, DOWNLOAD_INFORMATION_COLLECTED_FILE, params=params)

    file_url = response.get("data", "").get("url", "")
    expires = response.get("data", "").get("expires", "")
    password = response.get("data", "").get("password", "")
    filename = response.get("data", "").get("filename", "")
    message = {
        "url": file_url,
        "expires": expires,
        "password": password,
        "filename": filename,
    }
    results = CommandResults(
        readable_output=tableToMarkdown(TABLE_COLLECTED_FORENSIC_FILE_DOWNLOAD_INFORMATION, message, removeNull=True),
        outputs_prefix="VisionOne.Download_Information_For_Collected_Forensic_File",
        outputs_key_field="url",
        outputs=message,
    )
    return results


def submit_file_to_sandbox(client: Client, args: dict[str, Any]) -> str | CommandResults:
    """
    submit file to sandbox and sends the result to demist war room
    :type client: ``Client``
    :param client: client object to use http_request.
    :type args: ``dict``
    :param args: args object to fetch the argument data.
    :return: sends data to demisto war room.
    :rtype: ``dict`
    """
    data = {}
    params: dict[Any, Any] = {}
    file_url = args.get(FILE_URL)
    file_name = args.get(FILE_NAME)
    document_pass = args.get(DOCUMENT_PASSWORD)
    if document_pass:
        data["documentPassword"] = base64.b64encode(document_pass.encode(ASCII)).decode(ASCII)
    archive_pass = args.get(ARCHIVE_PASSWORD)
    if archive_pass:
        data["archivePassword"] = base64.b64encode(archive_pass.encode(ASCII)).decode(ASCII)
    headers = {AUTHORIZATION: f"{BEARER}{client.api_key}"}
    try:
        file_content = requests.get(file_url, allow_redirects=True)  # type: ignore
        files = {"file": (file_name, file_content.content, "application/x-zip-compressed")}
        result = requests.post(
            f"{client.base_url}{SUBMIT_FILE_TO_SANDBOX}",
            params=params,
            headers=headers,
            data=data,
            files=files,
        )
        result.raise_for_status()
    except HTTPError as http_err:
        demisto.error(http_err)
        return_error(http_err)
    except Exception as err:
        demisto.error(err)
        return_error(err)
    else:
        response = result.json()

    message = {
        "message": response.get("message", ""),
        "code": response.get("code", ""),
        "task_id": response.get("data", "").get("taskId", ""),
        "digest": response.get("data", "").get("digest", ""),
    }
    results = CommandResults(
        readable_output=tableToMarkdown(TABLE_SUBMIT_FILE_TO_SANDBOX, message, removeNull=True),
        outputs_prefix="VisionOne.Submit_File_to_Sandbox",
        outputs_key_field="message",
        outputs=message,
    )
    return results


def submit_file_entry_to_sandbox(client: Client, args: dict[str, Any]) -> str | CommandResults:
    entry = args.get(ENTRY_ID)
    file_ = demisto.getFilePath(entry)
    file_name = file_.get("name")
    file_path = file_.get("path")
    archive_pass = args.get(ARCHIVE_PASSWORD)
    document_pass = args.get(DOCUMENT_PASSWORD)
    query_params: dict[Any, Any] = {}
    headers = {AUTHORIZATION: f"{BEARER} {client.api_key}"}
    with open(file_path, "rb") as f:
        contents = f.read()
    data = {}
    if document_pass:
        data["documentPassword"] = base64.b64encode(document_pass.encode(ASCII)).decode(ASCII)
    if archive_pass:
        data["archivePassword"] = base64.b64encode(archive_pass.encode(ASCII)).decode(ASCII)
    files = {"file": (f"{file_name}", contents, "application/octet-stream")}
    try:
        result = requests.post(
            f"{client.base_url}{SUBMIT_FILE_TO_SANDBOX}",
            params=query_params,
            headers=headers,
            data=data,
            files=files,
        )
    except HTTPError as http_err:
        demisto.error(http_err)
        return_error(http_err)
    response = result.json()
    message = {
        "filename": file_name,
        "entryId": entry,
        "file_path": file_.get("path", ""),
        "message": response.get("message"),
        "task_id": response.get("data", {}).get("taskId", ""),
        "code": response.get("code", ""),
        "digest": response.get("data", {}).get("digest", {}),
    }
    results = CommandResults(
        readable_output=tableToMarkdown(TABLE_SUBMIT_FILE_ENTRY_TO_SANDBOX, message, removeNull=True),
        outputs_prefix="VisionOne.Submit_File_Entry_to_Sandbox",
        outputs_key_field="entryId",
        outputs=message,
    )
    return results


def add_note(client: Client, args: dict[str, Any]) -> str | CommandResults:
    """
    Adds a note to an existing workbench alert
    :type client: ``Client``
    :param client: client object to use http_request.
    :type args: ``dict``
    :param args: args object to fetch the argument data.
    :return: sends data to demisto war room.
    :rtype: ``dict`
    """
    workbench_id = args.get(WORKBENCH_ID)
    content = args.get(CONTENT)

    body = {"content": content}
    response = client.http_request(POST, ADD_NOTE_ENDPOINT.format(workbenchId=workbench_id), data=json.dumps(body))

    note_id = response.get("data").get("id")
    response_code = response.get("info").get("code")
    response_msg = response.get("info").get("msg")
    message = {
        "Workbench_Id": workbench_id,
        "noteId": note_id,
        "response_code": response_code,
        "response_msg": response_msg,
    }
    results = CommandResults(
        readable_output=tableToMarkdown(TABLE_ADD_NOTE, message, removeNull=True),
        outputs_prefix="VisionOne.Add_Note",
        outputs_key_field="noteId",
        outputs=message,
    )
    return results


def update_status(client: Client, args: dict[str, Any]) -> str | CommandResults:
    """
    Updates the status of an existing workbench alert
    :type client: ``Client``
    :param client: client object to use http_request.
    :type args: ``dict``
    :param args: args object to fetch the argument data.
    :return: sends data to demisto war room.
    :rtype: ``dict`
    """
    workbench_id = args.get(WORKBENCH_ID)
    status = args.get(STATUS)

    if status == "new":
        update_status = NEW
    elif status == "in_progress":
        update_status = IN_PROGRESS
    elif status == "resolved_true_positive":
        update_status = RESOLVED_TRUE_POSITIVE
    elif status == "resolved_false_positive":
        update_status = RESOLVED_FALSE_POSITIVE
    else:
        update_status = None
        demisto.debug(f"{status=} didn't match any condition. {update_status=}")

    body = {"investigationStatus": update_status}
    response = client.http_request(
        PUT,
        UPDATE_STATUS_ENDPOINT.format(workbenchId=workbench_id),
        data=json.dumps(body),
    )

    response_code = response.get("info").get("code")
    response_msg = response.get("info").get("msg")
    message = {
        "Workbench_Id": workbench_id,
        "response_code": response_code,
        "response_msg": response_msg,
    }
    results = CommandResults(
        readable_output=tableToMarkdown(TABLE_UPDATE_STATUS, message, removeNull=True),
        outputs_prefix="VisionOne.Update_Status",
        outputs_key_field="Workbench_Id",
        outputs=message,
    )
    return results


def main():
    try:
        """GLOBAL VARS"""
        params = demisto.params()

        base_url = params.get(URL)
        api_key = params.get(API_TOKEN).get("password")
        proxy = params.get("proxy", False)
        verify = not params.get("insecure", False)

        client = Client(base_url, api_key, proxy, verify)

        command = demisto.command()
        demisto.debug(COMMAND_CALLED.format(command=command))
        args = demisto.args()

        if command == "test-module":
            return_results(test_module(client))

        elif command == "fetch-incidents":
            return_results(fetch_incidents(client))

        elif command in (ADD_BLOCKLIST_COMMAND, REMOVE_BLOCKLIST_COMMAND):
            return_results(add_or_remove_from_block_list(client, command, args))

        elif command in (QUARANTINE_EMAIL_COMMAND, DELETE_EMAIL_COMMAND):
            return_results(quarantine_or_delete_email_message(client, command, args))

        elif command in (ISOLATE_ENDPOINT_COMMAND, RESTORE_ENDPOINT_COMMAND):
            return_results(isolate_or_restore_connection(client, command, args))

        elif command == TERMINATE_PROCESS_COMMAND:
            return_results(terminate_process(client, args))

        elif command in (ADD_EXCEPTION_LIST_COMMAND, DELETE_EXCEPTION_LIST_COMMAND):
            return_results(add_or_delete_from_exception_list(client, command, args))

        elif command == ADD_SUSPICIOUS_LIST_COMMAND:
            return_results(add_to_suspicious_list(client, args))

        elif command == DELETE_SUSPICIOUS_LIST_COMMAND:
            return_results(delete_from_suspicious_list(client, args))

        elif command == GET_FILE_ANALYSIS_STATUS:
            return_results(get_file_analysis_status(client, args))

        elif command == GET_FILE_ANALYSIS_REPORT:
            return_results(get_file_analysis_report(client, args))

        elif command == GET_ENDPOINT_INFO_COMMAND:
            return_results(get_endpoint_info(client, args))

        elif command == COLLECT_FILE:
            return_results(collect_file(client, args))

        elif command == DOWNLOAD_COLLECTED_FILE:
            return_results(download_information_collected_file(client, args))

        elif command == FILE_TO_SANDBOX:
            return_results(submit_file_to_sandbox(client, args))

        elif command == FILE_ENTRY_TO_SANDBOX:
            return_results(submit_file_entry_to_sandbox(client, args))

        elif command == SANDBOX_SUBMISSION_POLLING:
            if args.get("polling") == "true":
                cmd_res = get_sandbox_submission_status(args, client)
                if cmd_res is not None:
                    return_results(cmd_res)
            else:
                return_results(client.sandbox_submission_polling(args))

        elif command == UPDATE_STATUS:
            return_results(update_status(client, args))

        elif command == ADD_NOTE:
            return_results(add_note(client, args))

        elif command == CHECK_TASK_STATUS:
            if args.get("polling") == "true":
                cmd_res = get_task_status(args, client)
                if cmd_res is not None:
                    return_results(cmd_res)
            else:
                return_results(client.status_check(args))

        else:
            demisto.error(f"{command} command is not implemented.")
            raise NotImplementedError(f"{command} command is not implemented.")

    except Exception as error:
        demisto.error(COMMAND_EXECUTION_ERROR.format(error=error))


if __name__ in ["__main__", "builtin", "builtins"]:
    main()