Trend Micro Apex

TrendAI™ Apex One central automation to manage agents and User-Defined Suspicious Objects.

Endpoint · TrendAI™ Apex One

Details

IDTrend Micro Apex
ProviderTrendAI™
CategoryEndpoint
From Version5.0.0
Docker Imagedemisto/pycef:1.0.0.10133006
Supported ModulesAgentix XSIAM EDR Cortex Cloud Cloud Runtime Security

README

TrendAI™ Apex One central automation to manage agents and User-Defined Suspicious Objects

This integration was integrated and tested with version 2019 hotfix 5449 of TrendAI™ Apex One Central

Configure TrendAI™ Apex One in Cortex

Parameter Description Required
url Server URL (e.g. https://vxsuz5.manage.trendmicro.com) True
application_id Application ID True
token API Key True
insecure Trust any certificate (not secure) False
proxy Use system proxy settings False

Commands

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

trendmicro-apex-udso-list


Retrieve a list of User-Defined Suspicious Objects from the Apex One Central server.

Base Command

trendmicro-apex-udso-list

Input

Argument Name Description Required
type The suspicious object type to query Optional
content_filter Filters the list to suspicious objects that match the specified string Optional

Context Output

Path Type Description
TrendMicroApex.UDSO.type String Indicator type of the UDSO object, for example: ip, file, file_sha1, url, domain.
TrendMicroApex.UDSO.content String Indicator content of the UDSO object.
TrendMicroApex.UDSO.notes String Indicator notes of the UDSO object.
TrendMicroApex.UDSO.scan_action String Scan action of the UDSO object, for example: log, block, quarantine.
TrendMicroApex.UDSO.expiration_utc_date Date Expiration date of the UDSO object in UTC.

Command Example


#### Context Example

{
“TrendMicroApex”: {
“UDSO”: [
{
“content”: “A94A8FE5CCB19BA61C4C0873D391E987982FBBD3”,
“expiration_utc_date”: null,
“notes”: “Documentation”,
“scan_action”: “log”,
“type”: “file”
},
{
“content”: “8.8.8.8”,
“expiration_utc_date”: null,
“notes”: “Documentation”,
“scan_action”: “log”,
“type”: “ip”
}
]
}
}


#### Human Readable Output

>### Apex UDSO List
>
>|content|expiration_utc_date|notes|scan_action|type|
>|---|---|---|---|---|
>| A94A8FE5CCB19BA61C4C0873D391E987982FBBD3 |  | Documentation | log | file |
>| 8.8.8.8 |  | Documentation | log | ip |

### trendmicro-apex-udso-add

***
Add suspicious file SHA-1, IP address, domain, or URL objects to the User-Defined Suspicious Object list.

#### Base Command

`trendmicro-apex-udso-add`

#### Input

| **Argument Name** | **Description** | **Required** |
| --- | --- | --- |
| type | The suspicious object type | Required |
| content | The suspicious object content for the specified type, for example 8.8.8.8 (for type "file", provide the binary content of the suspicious file as a base64 string) | Required |
| scan_action | The scan action to perform on the suspicious object (The "quarantine" scan action is only available for file type objects) | Required |
| notes | Description of the object. | Optional |
| expiration | The UTC expiration date and time of the suspicious object, for example: 2020-01-25T09:00:00Z | Optional |

#### Context Output

There is no context output for this command.

#### Command Example

```!trendmicro-apex-udso-add type="ip" content="8.8.8.8" scan_action="log" notes="Documentation"```

#### Context Example

{}


#### Human Readable Output

>### UDSO "8.8.8.8" of type "ip" was added successfully with scan action "log"

### trendmicro-apex-udso-delete

***
Delete suspicious file SHA-1, IP address, domain, or URL objects from the User-Defined Suspicious Object list.

#### Base Command

`trendmicro-apex-udso-delete`

#### Input

| **Argument Name** | **Description** | **Required** |
| --- | --- | --- |
| type | The suspicious object type | Required |
| content | The suspicious object content for the specified type | Required |
| notes | Description of the object (maximum length: 256 characters). | Optional |
| scan_action | The scan action to perform on the suspicious object. | Optional |

#### Context Output

There is no context output for this command.

#### Command Example

```!trendmicro-apex-udso-delete type=ip content=8.8.8.8```

#### Context Example

{}


#### Human Readable Output

>### UDSO "8.8.8.8" of type "ip" was deleted successfully

### trendmicro-apex-isolate

***
Isolate an agent from the network

#### Base Command

`trendmicro-apex-isolate`

#### Input

| **Argument Name** | **Description** | **Required** |
| --- | --- | --- |
| multi_match | Whether to allow multiple matches or not. If this parameter is set to "false", and the provided parameters match multiple agents, the action will be unsuccessful. | Optional |
| entity_id | The GUID of the managed product agent | Optional |
| ip_address | The IP address of the managed product agent | Optional |
| mac_address | The MAC address of the managed product agent | Optional |
| host_name | The endpoint name of the managed product agent | Optional |
| product | The Trend Micro product on the server instance | Optional |

#### Context Output

There is no context output for this command.

#### Command Example

```!trendmicro-apex-isolate multi_match="true" ip_address="8.8.8.8"```

#### Context Example

{}


#### Human Readable Output

>### No agents were affected

### trendmicro-apex-restore

***
Restore an isolated agent connection to the network.

#### Base Command

`trendmicro-apex-restore`

#### Input

| **Argument Name** | **Description** | **Required** |
| --- | --- | --- |
| multi_match | Whether to allow multiple matches or not. If this argument is set to "false", and the provided parameters match multiple agents, the action will be unsuccessful. | Optional |
| entity_id | The GUID of the managed product agent | Optional |
| ip_address | The IP address of the managed product agent | Optional |
| mac_address | The MAC address of the managed product agent | Optional |
| host_name | The endpoint name of the managed product agent | Optional |
| product | The Trend Micro product on the server instance | Optional |

#### Context Output

There is no context output for this command.

#### Command Example

```!trendmicro-apex-restore multi_match="true" ip_address="8.8.8.8"```

#### Context Example

{}


#### Human Readable Output

>### No agents were affected

### trendmicro-apex-list-logs

***
Retrieves a maximum of 1000 logs of detection types from the server. The `Pattern Update Status` and `Engine Update Status` log types returns all logs (no maximum) from the specified "since_time". In some cases the command might return alerts that were created before the specified time. This is a known issue with the API.

#### Base Command

`trendmicro-apex-list-logs`

#### Input

| **Argument Name** | **Description** | **Required** |
| --- | --- | --- |
| page_token | The log ID of the first record to query. Note: For Pattern Update Status and Engine Update Status logs, the value of page_token must be "0". | Optional |
| since_time | The date/time of the first record to query, in one of the following formats:  '2020-06-21T08:00:00Z', 'Jun 21 2020 08:00:00 GMT+00:00'. In some cases the command might return logs that were created before the specified time. This is a known issue with the API. | Optional |
| log_type | The type of log data to retrieve. | Required |
| limit | The number of items to return. Default is 50. | Optional |

#### Context Output

| **Path** | **Type** | **Description** |
| --- | --- | --- |
| TrendMicroApex.Log.LogVersion | Number | The version of the log. |
| TrendMicroApex.Log.EventName | String | The name of the event. |
| TrendMicroApex.Log.EventID | String | The event ID. |
| TrendMicroApex.Log.ApplianceVersion | Number | The version of the appliance. |
| TrendMicroApex.Log.ApplianceProduct | String | The product name. |
| TrendMicroApex.Log.ApplianceVendor | String | The vendor name. |

#### Command Example

```!trendmicro-apex-list-logs log_type="Web Violation" limit=2 since_time="Jun 21 2020 07:56:09 GMT+00:00"```

#### Context Example

{
“TrendMicroApex”: {
“Log”: [
{
“ApplianceProduct”: “Apex Central”,
“ApplianceVendor”: “Trend Micro”,
“ApplianceVersion”: “2019”,
“CreationTime”: “Jun 21 2020 07:56:09 GMT+00:00”,
“EventID”: “WB:36”,
“EventName”: “36”,
“LogVersion”: “0”,
“SLF_PolicyName”: “Internal User Policy”,
“SLF_SeverityLevel”: “100 “,
“Severity”: “3”,
“Web_Reputation_Rating”: “49”,
“act”: “2”,
“app”: “5”,
“cat”: “36”,
“cnt”: “1”,
“deviceDirection”: “2”,
“deviceExternalId”: “1”,
“deviceFacility”: “Apex One”,
“deviceProcessName”: “C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe”,
“dpt”: “80”,
“duser”: “TRENDMICROAPEX-\\admin”,
“dvchost”: “CU-PRO1-8254-2”,
“request”: “http://www.eicar.org/download/eicar.com.txt”,
“shost”: “TRENDMICROAPEX-“,
“src”: “10.128.0.11”
},
{
“ApplianceProduct”: “Apex Central”,
“ApplianceVendor”: “Trend Micro”,
“ApplianceVersion”: “2019”,
“CreationTime”: “Jun 21 2020 07:56:28 GMT+00:00”,
“EventID”: “WB:36”,
“EventName”: “36”,
“LogVersion”: “0”,
“SLF_PolicyName”: “Internal User Policy”,
“SLF_SeverityLevel”: “100 “,
“Severity”: “3”,
“Web_Reputation_Rating”: “49”,
“act”: “2”,
“app”: “5”,
“cat”: “36”,
“cnt”: “1”,
“deviceDirection”: “2”,
“deviceExternalId”: “2”,
“deviceFacility”: “Apex One”,
“deviceProcessName”: “C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe”,
“dpt”: “80”,
“duser”: “TRENDMICROAPEX-\\admin”,
“dvchost”: “CU-PRO1-8254-2”,
“request”: “http://www.eicar.org/download/eicar.com”,
“shost”: “TRENDMICROAPEX-“,
“src”: “10.128.0.11”
}
]
}
}


#### Human Readable Output

>### Trend Micro Apex One - Web Violation Logs
>
>|EventName|EventID|CreationTime|LogVersion|ApplianceVersion|ApplianceProduct|ApplianceVendor|
>|---|---|---|---|---|---|---|
>| 36 | WB:36 | Jun 21 2020 07:56:09 GMT+00:00 | 0 | 2019 | Apex Central | Trend Micro |
>| 36 | WB:36 | Jun 21 2020 07:56:28 GMT+00:00 | 0 | 2019 | Apex Central | Trend Micro |

### trendmicro-apex-udso-file-add

***
Adds the uploaded file information to the User-Defined Suspicious Objects list. If the file already exists, it will be updated with the new arguments.

#### Base Command

`trendmicro-apex-udso-file-add`

#### Input

| **Argument Name** | **Description** | **Required** |
| --- | --- | --- |
| file_scan_action | The scan action to perform. Can be 'Log', 'Block' or 'Quarantine'. Default is 'Log'.  | Required |
| note | Additional information. | Optional |
| entry_id | The entry ID of the file to upload. | Required |

#### Context Output

There is no context output for this command.

#### Command Example

```!trendmicro-apex-udso-file-add entry_id=10378@f6e9c46f-e2e9-446f-8cd9-909bd5f72dbf file_scan_action=Log note="Documentation"```

#### Context Example

{}


#### Human Readable Output

>### The file "test" was added to the UDSO list successfully

### trendmicro-apex-managed-servers-list

***
Retrieves a list of managed product servers reporting to Apex One Central.

#### Base Command

`trendmicro-apex-managed-servers-list`

#### Input

| **Argument Name** | **Description** | **Required** |
| --- | --- | --- |
| entity_id | The GUID of the managed product server. | Optional |
| ip_address | The IP address of the endpoint. | Optional |
| mac_address | The MAC address of the endpoint. | Optional |
| host_name | The name of the endpoint. | Optional |
| product | The Trend Micro product name. | Optional |

#### Context Output

| **Path** | **Type** | **Description** |
| --- | --- | --- |
| TrendMicroApex.Server.entity_id | String | The GUID of the managed product server. |
| TrendMicroApex.Server.product | String | The Trend Micro product on the server instance. |
| TrendMicroApex.Server.ad_domain | String | The Active Directory domain that the server belongs to \(if applicable\). |
| TrendMicroApex.Server.ip_address_list | String | The IP address list on the server. |
| TrendMicroApex.Server.host_name | String | The hostname of the server. |
| TrendMicroApex.Server.capabilities | String | The API actions that can be performed on the server. |

#### Command Example

```!trendmicro-apex-managed-servers-list```

#### Context Example

{
“TrendMicroApex”: {
“Server”: [
{
“ad_domain”: “”,
“capabilities”: [],
“entity_id”: “E9DF20C5-F060-4BC5-8A4B-5452163A1C77”,
“host_name”: “cu-pro1-8254-2”,
“ip_address_list”: [
“8.8.8.8”
],
“product”: “SLF_PRODUCT_PLS_TMSM”
},
{
“ad_domain”: “”,
“capabilities”: [
“cmd_deploy_update_sources”
],
“entity_id”: “B220EB61-6240-44B4-9B94-4AC3F22E6A62”,
“host_name”: “CU-PRO1-8254-2”,
“ip_address_list”: [
“8.8.8.8”
],
“product”: “SLF_PRODUCT_OFFICESCAN_CE”
},
{
“ad_domain”: “”,
“capabilities”: [],
“entity_id”: “DA010000-0000-0004-6B00-FFFFFFFFFFFF”,
“host_name”: “”,
“ip_address_list”: [
“”
],
“product”: “SLF_PRODUCT_HEADLESS_DSM”
}
]
}
}


#### Human Readable Output

>### Trend Micro Apex Servers List
>
>|Entity Id|Product|Host Name|Ip Address List|Capabilities|
>|---|---|---|---|---|
>| E9DF20C5-F060-4BC5-8A4B-5452163A1C77 | SLF_PRODUCT_PLS_TMSM | cu-pro1-8254-2 | 8.8.8.8 |  |
>| B220EB61-6240-44B4-9B94-4AC3F22E6A62 | SLF_PRODUCT_OFFICESCAN_CE | CU-PRO1-8254-2 | 8.8.8.8 | cmd_deploy_update_sources |
>| DA010000-0000-0004-6B00-FFFFFFFFFFFF | SLF_PRODUCT_HEADLESS_DSM |  |  |  |

### trendmicro-apex-security-agents-list

***
Retrieves a list of Security Agents.

#### Base Command

`trendmicro-apex-security-agents-list`

#### Input

| **Argument Name** | **Description** | **Required** |
| --- | --- | --- |
| entity_id | The GUID of the Security Agent. | Optional |
| ip_address | The IP address of the endpoint. | Optional |
| mac_address | The MAC address of the endpoint. | Optional |
| host_name | The name of the endpoint. | Optional |
| product | The Trend Micro product name. | Optional |
| managing_server_id | The GUID of the product server that manages the Security Agent. | Optional |

#### Context Output

| **Path** | **Type** | **Description** |
| --- | --- | --- |
| TrendMicroApex.Agent.entity_id | String | The GUID of the Security Agent. |
| TrendMicroApex.Agent.product | String | The Trend Micro product name. |
| TrendMicroApex.Agent.managing_server_id | String | The GUID of the product server that manages the Security Agent. |
| TrendMicroApex.Agent.ad_domain | String | The Active Directory domain that the agent belongs to \(if applicable\). |
| TrendMicroApex.Agent.folder_path | String | The folder path of the agent in the machine. |
| TrendMicroApex.Agent.ip_address_list | String | The IP address list on the server. |
| TrendMicroApex.Agent.mac_address_list | String | The MAC address of the endpoint. |
| TrendMicroApex.Agent.host_name | String | The name of the endpoint. |
| TrendMicroApex.Agent.isolation_status | String | The isolation status of the agent. |
| TrendMicroApex.Agent.capabilities | String | Lists the API actions that can be performed on the agent. |

#### Command Example

```!trendmicro-apex-security-agents-list```

#### Context Example

{
“TrendMicroApex”: {
“Agent”: {
“ad_domain”: “”,
“capabilities”: [
“cmd_restore_isolated_agent”,
“cmd_isolate_agent”,
“cmd_relocate_agent”,
“cmd_uninstall_agent”
],
“entity_id”: “b59e624c-2cf0-4180-83d7-e08abbf9ad54”,
“folder_path”: “Workgroup”,
“host_name”: “TRENDMICROAPEX-“,
“ip_address_list”: [
“10.128.0.11”
],
“isolation_status”: “normal”,
“mac_address_list”: “42-01-0A-80-00-0B”,
“managing_server_id”: “B220EB61-6240-44B4-9B94-4AC3F22E6A62”,
“product”: “SLF_PRODUCT_OFFICESCAN_CE”
}
}
}


#### Human Readable Output

>### Trend Micro Apex One Agents List
>
>|Capabilities|Entity Id|Folder Path|Host Name|Ip Address List|Isolation Status|Mac Address List|Managing Server Id|Product|
>|---|---|---|---|---|---|---|---|---|
>| cmd_restore_isolated_agent,<br/>cmd_isolate_agent,<br/>cmd_relocate_agent,<br/>cmd_uninstall_agent | b59e624c-2cf0-4180-83d7-e08abbf9ad54 | Workgroup | TRENDMICROAPEX- | 8.8.8.8 | normal | 42-01-0A-80-00-0B | B220EB61-6240-44B4-9B94-4AC3F22E6A62 | SLF_PRODUCT_OFFICESCAN_CE |

### trendmicro-apex-endpoint-sensors-list

***
Retrieves a list of Security Agents with the Endpoint Sensor feature enabled.

#### Base Command

`trendmicro-apex-endpoint-sensors-list`

#### Input

| **Argument Name** | **Description** | **Required** |
| --- | --- | --- |
| limit | The number of records to retrieve. Default is 50. | Optional |
| offset | The page from which to start retrieving records. Default is 0. | Optional |
| filter_by_endpoint_name | Filter the agents by endpoint name (partial string match). | Optional |
| filter_by_endpoint_type | Filter the agents by endpoint type. Can be "Desktop" or "Server". | Optional |
| filter_by_ip_address | Filter the agents by endpoint IP address range represented by comma separated ranges list. Example: "Starting_IP_Address,Ending_IP_Address" | Optional |
| filter_by_operating_system | Filter the agents by operating system. | Optional |

#### Context Output

| **Path** | **Type** | **Description** |
| --- | --- | --- |
| TrendMicroApex.EndpointSensorSecurityAgent.agentGuid | String | The GUID of the agent. |
| TrendMicroApex.EndpointSensorSecurityAgent.serverGuid | String | The GUID of the server that manages the agent. |
| TrendMicroApex.EndpointSensorSecurityAgent.machineName | String | The hostname of the endpoint. |
| TrendMicroApex.EndpointSensorSecurityAgent.isImportant | Boolean | Whether the agent is tagged as important. |
| TrendMicroApex.EndpointSensorSecurityAgent.isOnline | Boolean | Whether the agent is online. |
| TrendMicroApex.EndpointSensorSecurityAgent.ip | String | The IP address of the agent. |
| TrendMicroApex.EndpointSensorSecurityAgent.machineGuid | String | The GUID of the endpoint. |
| TrendMicroApex.EndpointSensorSecurityAgent.machineType | String | The endpoint type. |
| TrendMicroApex.EndpointSensorSecurityAgent.machineLabels | Number | The machine labels. |
| TrendMicroApex.EndpointSensorSecurityAgent.machineOS | String | The operating system of the endpoint. |
| TrendMicroApex.EndpointSensorSecurityAgent.isolateStatus | String | The isolation status of the agent. |
| TrendMicroApex.EndpointSensorSecurityAgent.isEnable | Boolean | Whether the agent is enabled. |
| TrendMicroApex.EndpointSensorSecurityAgent.userName | String | The user name of the agent. |
| TrendMicroApex.EndpointSensorSecurityAgent.userGuid | String | The GUID of the user. |
| TrendMicroApex.EndpointSensorSecurityAgent.productType | Number | The Trend Micro product type on the server instance. |

#### Command Example

```!trendmicro-apex-endpoint-sensors-list```

#### Context Example

{
“TrendMicroApex”: {
“EndpointSensorSecurityAgent”: {
“agentGuid”: “b59e624c-2cf0-4180-83d7-e08abbf9ad54”,
“ip”: “8.8.8.8”,
“isEnable”: true,
“isImportant”: false,
“isOnline”: true,
“isolateStatus”: 0,
“machineGuid”: “4C80331A-E39E-4584-A1B7-5237B3F0F239”,
“machineLabels”: null,
“machineName”: “TRENDMICROAPEX-“,
“machineOS”: “Windows Server 2019”,
“machineType”: “Server”,
“productType”: 15,
“serverGuid”: “B220EB61-6240-44B4-9B94-4AC3F22E6A62”,
“userGuid”: “DC15EA904-03CC-E3A2-9CC0-BA57D814772”,
“userName”: “TRENDMICROAPEX-\admin”
}
}
}


#### Human Readable Output

>### Trend Micro Apex Security Agents with Endpoint Sensor enabled
>
>|agentGuid|ip|isEnable|isImportant|isOnline|isolateStatus|machineGuid|machineName|machineOS|machineType|productType|serverGuid|userGuid|userName|
>|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
>| b59e624c-2cf0-4180-83d7-e08abbf9ad54 | 8.8.8.8 | true | false | true | 0 | 4C80331A-E39E-4584-A1B7-5237B3F0F239 | TRENDMICROAPEX- | Windows Server 2019 | Server | 15 | B220EB61-6240-44B4-9B94-4AC3F22E6A62 | DC15EA904-03CC-E3A2-9CC0-BA57D814772 | TRENDMICROAPEX-\admin |

### trendmicro-apex-historical-investigation-create

***
Creates a new historical investigation on all Security Agents with Endpoint Sensor enabled using the specified criteria, search operator, and match condition.

#### Base Command

`trendmicro-apex-historical-investigation-create`

#### Input

| **Argument Name** | **Description** | **Required** |
| --- | --- | --- |
| file_name_contains | Filter by file name (partial string match). | Optional |
| file_name_is | Filter by file name (exact match). Supports comma-separated values. | Optional |
| file_path_is | Filter by file path (exact match). Supports comma separated values. | Optional |
| account_contains | Filter by account (partial string match). Supports comma separated values. | Optional |
| account_is | Filter by account (exact match). Supports comma separated values. | Optional |
| command_line_contains | Filter by command line (partial string match). Supports comma separated values. | Optional |
| command_line_is | Filter by command line (exact match). Supports comma separated values. list. | Optional |
| registry_key_contains | Filter by registry key (partial string match). Supports comma separated values. | Optional |
| registry_key_is | Filter by registry key (exact match). Supports comma separated values. list. | Optional |
| registry_name_contains | Filter by registry name (partial string match). Supports comma separated values. | Optional |
| registry_name_is | Filter by registry name (exact match). Supports comma separated values. list. | Optional |
| registry_data_contains | Filter by registry data (partial string match). Supports comma separated values. | Optional |
| registry_data_is | Filter by registry data (exact match). Supports comma separated values. list. | Optional |
| host_name_contains | Filter by host name (partial string match). Supports comma separated values. | Optional |
| host_name_is | Filter by host name - (exact match). Supports comma separated values. | Optional |
| file_path_contains | Filter by file path (partial string match). Supports comma separated values. | Optional |
| operator | Operator used in the investigation. 'AND' - return endpoints that match all the criteria specified. 'OR' - return endpoints that match one of the specified criteria. | Required |
| criteria_kvp | Criteria string to show in the auditing log. | Optional |
| criteria_source | The source of criteria used to store the record in BIF. The default value is 0 (UNKNOWN). | Optional |
| search_period | Scope of the search results. For example, if the value is Three months, perform assessment on data within the last 90 days only. Can be "Default", "All", "One month", "Three months", "Six months", or "Twelve months". | Optional |

#### Context Output

| **Path** | **Type** | **Description** |
| --- | --- | --- |
| TrendMicroApex.HistoricalInvestigation.taskId | String | Task ID received from the investigation creation request. |
| TrendMicroApex.HistoricalInvestigation.lastContentId | String | ID used to retrieve the next set of results. |
| TrendMicroApex.HistoricalInvestigation.hasMore | Boolean | Whether the source has more results. |
| TrendMicroApex.HistoricalInvestigation.serverName | String | The name of the server. |
| TrendMicroApex.HistoricalInvestigation.serverGuid | String | The GUID of the server. |

#### Command Example

```!trendmicro-apex-historical-investigation-create operator=OR file_name_is=notepad.exe criteria_kvp="File name: notepad.exe" criteria_source=56 search_period="Twelve months"```

#### Context Example

{
“TrendMicroApex”: {
“HistoricalInvestigation”: {
“content”: [],
“hasMore”: false,
“lastContentId”: “”,
“serverGuid”: “B220EB61-6240-44B4-9B94-4AC3F22E6A62”,
“serverName”: “Apex One as a Service”,
“taskId”: “16545889-7708-48BF-BDFC-53A9E2A6942A”
}
}
}


#### Human Readable Output

>### The historical investigation was created successfully
>
>|taskId|serverName|serverGuid|
>|---|---|---|
>| 16545889-7708-48BF-BDFC-53A9E2A6942A | Apex One as a Service | B220EB61-6240-44B4-9B94-4AC3F22E6A62 |

### trendmicro-apex-investigation-result-list

***
Retrieves a list of all investigation results.

#### Base Command

`trendmicro-apex-investigation-result-list`

#### Input

| **Argument Name** | **Description** | **Required** |
| --- | --- | --- |
| limit | The number of records to retrieve. Default is 50. | Optional |
| offset | The page from which to start retrieving records. Default is 0. | Optional |
| scan_schedule_id | The scan schedule ID of the investigation to retrieve. | Optional |
| filter_by_task_name | Filter the results by task name (partial string match). | Optional |
| filter_by_creator_name | Filter the results by creator name (partial string match). | Optional |
| filter_by_scan_type | Filter the results by the scan method type. Can be "Search Windows registry", "Memory scan using YARA", or "Disk scan using OpenIOC". | Optional |
| filter_by_criteria_name | Filter the results by criteria name (partial string match). | Optional |
| scan_type | The method used for the investigation. Supports comma-separated values. Possible values are: "Windows registry", "YARA rule file", "IOC rule file", and "Disk IOC rule file". | Required |

#### Context Output

| **Path** | **Type** | **Description** |
| --- | --- | --- |
| TrendMicroApex.InvestigationResult.scanSummaryId | Number | The ID of the investigation. |
| TrendMicroApex.InvestigationResult.scanSummaryGuid | String | The GUID of the investigation. |
| TrendMicroApex.InvestigationResult.status | Number | Status of the investigation. |
| TrendMicroApex.InvestigationResult.statusForUI | Number | Status of the investigation shown in the web console. |
| TrendMicroApex.InvestigationResult.scanType | String | Method used for the investigation. |
| TrendMicroApex.InvestigationResult.submitTime | Date | Date and time when the investigation was submitted. |
| TrendMicroApex.InvestigationResult.finishTime | Date | Date and time when the investigation was finished. |
| TrendMicroApex.InvestigationResult.specificAgentType | Number | Indicates how targets were selected for the investigation. 0-All, 1-Specific. |
| TrendMicroApex.InvestigationResult.progressInfo.safeCount | Number | Number of agents with "No match" status. |
| TrendMicroApex.InvestigationResult.progressInfo.riskCount | Number | Number of agents with "Matched" status. |
| TrendMicroApex.InvestigationResult.progressInfo.pendingCount | Number | Number of agents with "Pending" status. |
| TrendMicroApex.InvestigationResult.progressInfo.timeoutCount | Number | Number of agents with "Timeout" status. |
| TrendMicroApex.InvestigationResult.progressInfo.noneCount | Number | Number of agents with "None" status. |
| TrendMicroApex.InvestigationResult.progressInfo.processingCount | Number | Number of agents with "Processing" status. |
| TrendMicroApex.InvestigationResult.progressInfo.errorCount | Number | Number of agents with errors. |
| TrendMicroApex.InvestigationResult.progressInfo.abortCount | Number | Number of aborted agents. |
| TrendMicroApex.InvestigationResult.progressInfo.connectionFailCount | Number | Number of agents that fail to connect. |
| TrendMicroApex.InvestigationResult.name | String | The name of the investigation. |
| TrendMicroApex.InvestigationResult.agentCount | Number | The number of agents in the investigation. |
| TrendMicroApex.InvestigationResult.matchedAgentCount | Number | The number of matched agents in the investigation. |
| TrendMicroApex.InvestigationResult.serverGuidList | String | Trend Micro GUID list of the servers. |
| TrendMicroApex.InvestigationResult.creator | String | The name of the user who created the investigation. |
| TrendMicroApex.InvestigationResult.scanCriteriaEntity.criteriaId | Number | Unique identifier used by the server to store the criteria. |
| TrendMicroApex.InvestigationResult.scanCriteriaEntity.criteriaName | String | The name of the criteria. |
| TrendMicroApex.InvestigationResult.scanCriteriaEntity.criteriaContent | String | The the criteria used to perform "registry" investigation. |
| TrendMicroApex.InvestigationResult.errorServers | String | Error response if server communication is unsuccessful. |

#### Command Example

```!trendmicro-apex-investigation-result-list scan_status=All scan_type="YARA rule file"```

#### Context Example

{
“TrendMicroApex”: {
“InvestigationResult”: {
“agentCount”: 1,
“creator”: “Cortex XSOAR-PANW”,
“errorServers”: “[]”,
“finishTime”: “1969-12-31T23:59:59+00:00”,
“matchedAgentCount”: 0,
“name”: “this is a test”,
“progressInfo”: {
“abortCount”: 0,
“connectionFailCount”: 0,
“errorCount”: 0,
“noneCount”: 0,
“pendingCount”: 0,
“processingCount”: 1,
“riskCount”: 0,
“safeCount”: 0,
“timeoutCount”: 0
},
“scanCriteriaEntity”: {
“criteriaContent”: “”,
“criteriaId”: 2,
“criteriaName”: “test.yar”
},
“scanSummaryGuid”: “6161bf10-f073-4762-bd10-088b0f68ad1d”,
“scanSummaryId”: 3,
“scanType”: “YARA rule file”,
“serverGuidList”: [
“B220EB61-6240-44B4-9B94-4AC3F22E6A62”
],
“specificAgentType”: 1,
“status”: “Running”,
“statusForUI”: “Running”,
“submitTime”: “2020-09-13T12:24:05+00:00”
}
}
}
```

Human Readable Output

Investigation result list

name scanSummaryId scanSummaryGuid submitTime serverGuidList creator
this is a test 3 6161bf10-f073-4762-bd10-088b0f68ad1d 2020-09-13T12:24:05+00:00 B220EB61-6240-44B4-9B94-4AC3F22E6A62 Cortex XSOAR-PANW

Configuration parameters

  • url — Server URL (e.g. https://vxsuz5.manage.trendmicro.com) (required)
  • application_id — Application ID (required)
  • token — API Key
  • credentials_api_token
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings

Commands (15)

  • trendmicro-apex-endpoint-sensors-list

    Retrieves a list of Security Agents with the Endpoint Sensor feature enabled.

  • trendmicro-apex-historical-investigation-create

    Creates a new historical investigation on all Security Agents with Endpoint Sensor enabled using the specified criteria, search operator, and match condition.

  • trendmicro-apex-investigation-result-list

    Retrieves a list of all investigation results.

  • trendmicro-apex-isolate

    Isolate an agent from the network.

  • trendmicro-apex-list-logs

    Retrieves a maximum of 1000 logs of detection types from the server. The `Pattern Update Status` and `Engine Update Status` log types returns all logs (no maximum) from the specified "since_time". In some cases the command might return alerts that were created before the specified time. This is a known issue with the API.

  • trendmicro-apex-managed-servers-list

    Retrieves a list of managed product servers reporting to Apex One Central.

  • trendmicro-apex-restore

    Restore an isolated agent connection to the network.

  • trendmicro-apex-security-agents-list

    Retrieves a list of Security Agents.

  • trendmicro-apex-udso-add

    Add suspicious file SHA-1, IP address, domain, or URL objects to the User-Defined Suspicious Object list.

  • trendmicro-apex-udso-delete

    Delete suspicious file SHA-1, IP address, domain, or URL objects from the User-Defined Suspicious Object list.

  • trendmicro-apex-udso-file-add

    Adds the uploaded file information to the User-Defined Suspicious Objects list. If the file already exists, it will be updated with the new arguments.

  • trendmicro-apex-udso-list

    Retrieve a list of User-Defined Suspicious Objects from the Apex One Central server.

  • trendmicro-apex-usdo-add Deprecated

    Add suspicious file, file SHA-1, IP address, domain, or URL objects to the User-Defined Suspicious Object list.

  • trendmicro-apex-usdo-delete Deprecated

    Delete suspicious file SHA-1, IP address, domain, or URL objects from the User-Defined Suspicious Object list.

  • trendmicro-apex-usdo-list Deprecated

    Retrieve a list of User-Defined Suspicious Objects from the Apex One Central server.

import demistomock as demisto
from CommonServerPython import *

from CommonServerUserPython import *

""" IMPORTS """
import base64
import hashlib
import json
import time
from datetime import UTC, datetime

import jwt
import pycef
import urllib3
from dateutil.parser import parse

# Disable insecure warnings
urllib3.disable_warnings()
""" CLIENT CLASS"""

UDSOAPIPATH = "/WebApp/api/SuspiciousObjects/UserDefinedSO"
PRODAGENTAPIPATH = "/WebApp/API/AgentResource/ProductAgents"
FIELDS_TO_REMOVE_FROM_CONTEXT = ["FeatureCtrl", "Meta", "PermissionCtrl", "SystemCtrl"]

CEF_HEADERS_TO_TREND_MICRO_HEADERS = {
    "CEFVersion": "LogVersion",
    "Name": "EventName",
    "DeviceEventClassID": "EventID",
    "DeviceVersion": "ApplianceVersion",
    "DeviceProduct": "ApplianceProduct",
    "DeviceVendor": "ApplianceVendor",
}

LOG_NAME_TO_LOG_TYPE = {
    "Data Loss Prevention": "data_loss_prevention",
    "Device Control": "device_access_control",
    "Behavior Monitoring": "behaviormonitor_rule",
    "Virus/Malware": "officescan_virus",
    "Spyware/Grayware": "spyware",
    "Web Violation": "web_security",
    "Content Violation": "security",
    "Network Content Inspection": "ncie",
    "C&C Callback": "cncdetection",
    "Suspicious File Information": "filehashdetection",
    "Predictive Machine Learning": "Predictive_Machine_Learning",
    "Virtual Analyzer Detections": "Sandbox_Detection_Log",
    "Application Control": "EACV_Information",
    "Managed Product User Access": "Managed_Product_Logged_Information",
    "Attack Discovery": "Attack_Discovery_Detections",
    "Pattern Update Status": "pattern_updated_status",
    "Engine Update Status": "engine_updated_status",
    "Product Auditing Events": "product_auditing_events",
    "Intrusion Prevention": "intrusion_prevention",
}

OS_NAME_TO_OS_TYPE = {
    "Windows XP": "WIN_XP",
    "Windows Vista": "WIN_VISTA",
    "Windows 7": "WIN_7",
    "Windows 8": "WIN_8",
    "Windows 10": "WIN_10",
    "Windows 2000": "WIN_2000",
    "Windows 2003": "WIN_2003",
    "Windows 2008": "WIN_2008",
    "Windows 2012": "WIN_2012",
    "Windows 2016": "WIN_2016",
    "iOS": "IOS",
    "Mac OS": "MAC_OS",
    "Android": "ANDROID",
    "Symbian": "SYMBIAN",
    "Windows Mobile": "WIN_MOBILE",
    "Windows General": "WIN",
}

SECURITY_AGENTS_TYPE_TO_NUMBER = {
    "endpoint_name": 1,
    "endpoint_type": 2,
    "endpoint_ip_address": 4,
    "endpoint_user_name": 6,
    "endpoint_OS": 5,
    "partial_OS": 9,
}

SCAN_TYPE_TO_NUM = {"Custom criteria": 0, "Windows registry": 1, "YARA rule file": 2, "IOC rule file": 3, "Disk IOC rule file": 6}

SCAN_NUM_TO_TYPE = {0: "Custom criteria", 1: "Windows registry", 2: "YARA rule file", 3: "IOC rule file", 6: "Disk IOC rule file"}

SCAN_STATUS_TO_NUM = {"All": 1, "Matched": 2, "No match": 3, "Pending": 4, "Unsuccessful": 5}

CUSTOM_INVESTIGATION_TYPE_TO_ID = {
    "file_name": 3,
    "file_path": 4,
    "account": 7,
    "command_line": 8,
    "registry_key": 9,
    "registry_name": 10,
    "registry_data": 11,
    "host_name": 12,
}

REPEAT_TYPE_TO_ID = {"Yearly": 1, "Monthly": 2, "Daily": 3}
GENERAL_INVESTIGATION_ARGS = [
    "agent_guids",
    "server_guids",
    "investigation_name",
    "scan_type",
    "time_range_type",
    "time_range_end",
    "time_range_start",
    "scan_schedule_guid",
    "scan_schedule_Id",
]

SEARCH_PERIOD_NAME_TYPE_TO_NUM = {
    "Default": -1,
    "All": 0,
    "One month": 1,
    "Three months": 3,
    "Six months": 6,
    "Twelve months": 12,
}

INVESTIGATION_RESULT_FILTER_TYPE_TO_NUM = {"task_name": 10, "creator_name": 11, "scan_type": 12, "criteria_name": 14}

INVESTIGATION_RESULT_SCAN_TYPE_TO_NUM = {"Search Windows registry": 1, "Memory scan using YARA": 2, "Disk scan using OpenIOC": 6}

AGENT_ISOLATION_STATUS_NUM_TO_VALUE = {
    0: "Unknown",
    1: "Normal",
    2: "Isolated",
    3: "Isolate command sent -pending",
    4: "Restore agent from isolation -pending",
}

INVESTIGATION_STATUS_NUM_TO_VALUE = {
    0: "Pending",
    1: "Running",
    2: "Cancel",
    3: "Complete",
    4: "Invalid",
    5: "Purged",
    6: "Error (All servers failed)",
}


class Client(BaseClient):
    def __init__(self, base_url, api_key, app_id, verify, proxy):
        super().__init__(base_url=base_url, verify=verify, proxy=proxy)
        self.base_url = base_url
        self.api_key = api_key
        self.application_id = app_id
        self.suffix = ""

    @staticmethod
    def __create_checksum(http_method, api_path, headers, request_body):
        string_to_hash = http_method.upper() + "|" + api_path.lower() + "|" + headers + "|" + request_body
        base64_string = base64.b64encode(hashlib.sha256(str.encode(string_to_hash)).digest()).decode("utf-8")
        return base64_string

    def create_jwt_token(
        self,
        http_method,
        api_path,
        headers,
        request_body,
        iat=time.time(),
        algorithm="HS256",
        version="V1",
    ):
        checksum = self.__create_checksum(http_method, api_path, headers, request_body)

        payload = {"appid": self.application_id, "iat": iat, "version": version, "checksum": checksum}
        token = jwt.encode(payload, self.api_key, algorithm=algorithm)
        return token

    def udso_list(self, list_type="", contentfilter=""):
        querystring = "?type=" + list_type + "&contentFilter=" + contentfilter
        headers = {
            "Authorization": "Bearer "
            + self.create_jwt_token(http_method="GET", api_path=UDSOAPIPATH + querystring, headers="", request_body="")
        }
        response = self._http_request("GET", UDSOAPIPATH, full_url=self.base_url + UDSOAPIPATH + querystring, headers=headers)
        return response

    def udso_delete(self, list_type="", content=""):
        querystring = "?type=" + list_type + "&content=" + content
        headers = {
            "Authorization": "Bearer "
            + self.create_jwt_token(http_method="DELETE", api_path=UDSOAPIPATH + querystring, headers="", request_body="")
        }
        response = self._http_request("DELETE", UDSOAPIPATH, full_url=self.base_url + UDSOAPIPATH + querystring, headers=headers)
        return response

    def udso_add(self, add_type=None, content=None, scan_action=None, notes="", expiration=""):
        if add_type and content and scan_action:
            req_body = {
                "param": {
                    "type": add_type,
                    "content": content,
                    "notes": notes,
                    "scan_action": scan_action,
                    "expiration_utc_date": expiration,
                }
            }

            headers = {
                "Content-Type": "application/json;charset=utf-8",
                "Authorization": "Bearer "
                + self.create_jwt_token(
                    http_method="PUT", api_path=UDSOAPIPATH + "/", headers="", request_body=json.dumps(req_body)
                ),
            }
            response = self._http_request(
                "PUT", UDSOAPIPATH + "/", full_url=self.base_url + UDSOAPIPATH + "/", headers=headers, data=json.dumps(req_body)
            )
            if response.get("Meta", {}).get("ErrorCode", "") != 0:
                raise ValueError(f'Operation failed - {response.get("Meta", {}).get("ErrorMsg")}')

            return response
        return None

    def udso_add_file(self, file_content_base64_string, file_name, file_scan_action, note: str = ""):
        payload = {
            "file_name": file_name,
            "file_content_base64_string": file_content_base64_string,
            "file_scan_action": file_scan_action,
            "note": note if note else "",
        }

        headers = {
            "Content-Type": "application/json;charset=utf-8",
            "Authorization": "Bearer "
            + self.create_jwt_token(http_method="PUT", api_path=self.suffix, headers="", request_body=json.dumps(payload)),
        }
        response = self._http_request("PUT", self.suffix, headers=headers, data=json.dumps(payload))
        if response.get("result_code", "") != 1:
            err_msg = f'Operation failed - {response.get("result_description", "")}'
            raise ValueError(err_msg)
        return response

    def _prodagent_command(self, action, multi_match=False, entity_id="", ip_add="", mac_add="", host="", prod=""):
        act = action

        req_body = {
            "act": act,
            "allow_multiple_match": multi_match,
            "entity_id": entity_id,
            "ip_address": ip_add,
            "mac_address": mac_add,
            "host_name": host,
            "product": prod,
        }

        headers = {
            "Content-Type": "application/json;charset=utf-8",
            "Authorization": "Bearer "
            + self.create_jwt_token(
                http_method="POST", api_path=PRODAGENTAPIPATH + "/", headers="", request_body=json.dumps(req_body)
            ),
        }
        response = self._http_request(
            "POST",
            PRODAGENTAPIPATH + "/",
            full_url=self.base_url + PRODAGENTAPIPATH + "/",
            headers=headers,
            data=json.dumps(req_body),
        )
        if response.get("result_code") != 1:
            err_msg = f'Operation failed - {response.get("result_description", "")}'
            raise ValueError(err_msg)
        return response

    def prodagent_isolate(self, multi_match=False, entity_id="", ip_add="", mac_add="", host="", prod=""):
        action = "cmd_isolate_agent"
        return self._prodagent_command(action, multi_match, entity_id, ip_add, mac_add, host, prod)

    def prodagent_restore(self, multi_match=False, entity_id="", ip_add="", mac_add="", host="", prod=""):
        action = "cmd_restore_isolated_agent"
        return self._prodagent_command(action, multi_match, entity_id, ip_add, mac_add, host, prod)

    @staticmethod
    def verify_format_and_convert_to_timestamp(since_time: str):
        if since_time == "0":  # '0' is the default timestamp
            return since_time
        if not (since_time.endswith(("GMT+00:00", "Z"))):
            raise ValueError(
                "'since_time' argument should be in one of the following formats:"
                "'2020-06-21T08:00:00Z', 'Jun 21 2020 08:00:00 GMT+00:00'"
            )

        since_time_timestamp = int(parse(since_time).timestamp())
        return since_time_timestamp

    def logs_list(self, log_type: str, since_time: str = "0", page_token: str = "0"):
        log_type = LOG_NAME_TO_LOG_TYPE.get(log_type)
        if log_type in ["pattern_updated_status", "engine_updated_status"] and page_token != "0":
            raise ValueError(
                "For 'Pattern Update Status' and 'Engine Update Status' log types, \nthe value of page_token must be '0'."
            )
        since_time_timestamp = self.verify_format_and_convert_to_timestamp(since_time)
        querystring = f"?output_format=1&page_token={page_token}&since_time={since_time_timestamp}"
        request_suffix = f"{self.suffix}/{log_type}{querystring}"
        jwt_token = self.create_jwt_token(http_method="GET", api_path=request_suffix, headers="", request_body="")

        headers = {"Authorization": "Bearer " + jwt_token, "Content-Type": "application/json;charset=utf-8"}

        response = self._http_request("GET", url_suffix=request_suffix, headers=headers)
        return response

    @staticmethod
    def convert_timestamps_and_scan_type_to_readable(results_list):
        """
        For every item in the list, convert the time values and the scan_type values to human readable
        Args:
            results_list: List of results returned from the API

        Returns:
            list. The updated list with the readable time and type values
        """
        time_keys = ["triggerTime", "submitTime", "finishTime"]
        status_keys = ["status", "statusForUI"]
        for result in results_list:
            for time_key in time_keys:
                if result.get(time_key):
                    result[time_key] = datetime.fromtimestamp(result.get(time_key), UTC).isoformat()
            for status_key in status_keys:
                if result.get(status_key):
                    result[status_key] = INVESTIGATION_STATUS_NUM_TO_VALUE[result.get(status_key)]
            if result.get("scanType"):
                result["scanType"] = SCAN_NUM_TO_TYPE[result["scanType"]]
        return results_list

    @staticmethod
    def remove_unnecessary_fields_from_response(response):
        for field in FIELDS_TO_REMOVE_FROM_CONTEXT:
            if response.get(field):
                response.pop(field)
        return response

    @staticmethod
    def build_query_string(entity_id="", ip_address="", mac_address="", host_name="", product="", managing_server_id=""):
        query_string = ""
        if entity_id:
            query_string += f"&entity_id={entity_id}"
        if ip_address:
            query_string += f"&ip_address={ip_address}"
        if mac_address:
            query_string += f"&mac_address={mac_address}"
        if host_name:
            query_string += f"&host_name={host_name}"
        if product:
            query_string += f"&product={product}"
        if managing_server_id:
            query_string += f"&managing_server_id={managing_server_id}"

        query_string = f"?{query_string[1:]}" if query_string else query_string

        return query_string

    def servers_or_agents_list(
        self, entity_id="", ip_address="", mac_address="", host_name="", product="", managing_server_id=""
    ):
        querystring = self.build_query_string(entity_id, ip_address, mac_address, host_name, product, managing_server_id)
        suffix = f"{self.suffix}{querystring}"
        jwt_token = self.create_jwt_token(http_method="GET", api_path=suffix, headers="", request_body="")
        headers = {"Authorization": "Bearer " + jwt_token, "Content-Type": "application/json;charset=utf-8"}

        response = self._http_request("GET", url_suffix=suffix, headers=headers)
        if response.get("result_code") != 1:
            err_msg = f'Operation failed - {response.get("result_description", "")}'
            raise ValueError(err_msg)
        return response

    @staticmethod
    def create_filter_entry(entry_value, entry_type, type_transformer: dict = SECURITY_AGENTS_TYPE_TO_NUMBER):
        """
        Create a filter entry - a dict with 'type', 'value' keys
        Args:
            entry_value: the value for the filter
            entry_type: the type of the filter
            type_transformer: transformer dict for the type value

        Returns:
            filter entry from the form :
            {
                "type" : type,
                "value": value
            }

        """
        filter_entry = {"type": type_transformer[entry_type]}
        if "OS" in entry_type:
            filter_entry["value"] = OS_NAME_TO_OS_TYPE[entry_value]  # type: ignore
        if entry_type == "scan_type":
            filter_entry["value"] = INVESTIGATION_RESULT_SCAN_TYPE_TO_NUM[entry_value]
        else:
            filter_entry["value"] = entry_value

        return filter_entry

    def create_payload_filter(self, endpoint_name="", endpoint_type="", ip_address="", operating_system=""):
        payload_filter = []
        if endpoint_name:
            payload_filter.append(self.create_filter_entry(entry_value=endpoint_name, entry_type="endpoint_name"))
        if endpoint_type:
            payload_filter.append(self.create_filter_entry(entry_value=endpoint_type, entry_type="endpoint_type"))
        if ip_address:
            ip_range = argToList(ip_address)
            payload_filter.append(self.create_filter_entry(entry_value=ip_range, entry_type="endpoint_ip_address"))
        if operating_system:
            if operating_system == "Windows General":
                # special case - filter by all windows security agents
                payload_filter.append(self.create_filter_entry(entry_value=operating_system, entry_type="partial_OS"))
            else:
                payload_filter.append(self.create_filter_entry(entry_value=operating_system, entry_type="endpoint_OS"))

        return payload_filter

    def endpoint_sensors_list(
        self,
        limit=50,
        offset=0,
        filter_by_endpoint_name="",
        filter_by_endpoint_type="",
        filter_by_ip_address="",
        filter_by_operating_system="",
    ):
        payload_data = {"pagination": {"limit": int(limit), "offset": int(offset)}}

        payload_filter = self.create_payload_filter(
            filter_by_endpoint_name, filter_by_endpoint_type, filter_by_ip_address, filter_by_operating_system
        )
        if payload_filter:
            payload_data["filter"] = payload_filter

        # return_error(payload_data)
        request_data = {
            "Url": "V1/Task/ShowAgentList",
            "TaskType": 4,  # For Endpoint Sensor, the value is always 4.
            "Payload": payload_data,
        }

        headers = {
            "Content-Type": "application/json;charset=utf-8",
            "Authorization": "Bearer "
            + self.create_jwt_token(http_method="PUT", api_path=self.suffix, headers="", request_body=json.dumps(request_data)),
        }

        response = self._http_request("PUT", self.suffix, headers=headers, data=json.dumps(request_data))
        self.validate_response(response, error_message="Endpoint sensors list operation failed")
        return response

    @staticmethod
    def fix_log_headers(log: dict):
        """
        Fix the cef log headers according to TrendMicro headers
        Args:
            log(dict): the cef log to fix.

        Returns:
            the fixed log with the correct headers mapping.

        """

        remove_from_dict = ["DeviceName", "DeviceSeverity"]  # duplicate keys from pycef - backwards compatibility reasons
        keys_to_fix = CEF_HEADERS_TO_TREND_MICRO_HEADERS.keys()

        # remove unnecessary keys
        for key in remove_from_dict:
            log.pop(key, None)

        # fix the keys to their correct name
        new_log = log.copy()
        for key in log:
            if key in keys_to_fix:
                new_log[CEF_HEADERS_TO_TREND_MICRO_HEADERS[key]] = new_log.pop(key)
            if key == "rt":  # this key is always referencing to 'Creation Time' header
                new_log["CreationTime"] = new_log.pop("rt")

        return new_log

    def parse_cef_logs_to_dict_logs(self, response):
        logs_list_in_cef_format = response.get("Data", {}).get("Logs", [])
        parsed_logs_list = []
        for log in logs_list_in_cef_format:
            parsed_log = pycef.parse(log)
            if parsed_log:
                parsed_trendmicro_log = self.fix_log_headers(parsed_log)
                parsed_logs_list.append(parsed_trendmicro_log)

        return parsed_logs_list

    @staticmethod
    def update_agents_info_in_payload(payload_data, agent_guids):
        agent_guids_dict = json.loads(agent_guids)  # this is a dict of { server_guids : [agent_guids] }
        payload_data["agentGuid"] = agent_guids_dict
        payload_data["serverGuid"] = list(agent_guids_dict.keys())

        return payload_data

    @staticmethod
    def validate_response(response, error_message):
        response_message = response.get("Data", {}).get("Message", "")
        if response_message and response_message != "OK":
            raise DemistoException(f"{error_message}. Reason:\n{response_message}")

    @staticmethod
    def get_file_name_and_base_64_content(entry_id: str):
        file = demisto.getFilePath(entry_id)
        file_path = file["path"]
        file_name = file["name"]
        with open(file_path, "rb") as f:
            file_content_base64_string = base64.b64encode(f.read()).decode()  # the api is expecting 64based encoded file
        return file_name, file_content_base64_string

    @staticmethod
    def create_custom_criteria(custom_investigation_args):
        custom_criteria = {"operator": custom_investigation_args.pop("operator")}
        filters = []

        for key, value in custom_investigation_args.items():
            if key.endswith("is"):
                type_id = CUSTOM_INVESTIGATION_TYPE_TO_ID[key[:-3]]  # arg name is : 'NAME_is', pass NAME
                filters.append({"condition": "IS", "value": argToList(value), "typeId": type_id})
            if key.endswith("contains"):
                type_id = CUSTOM_INVESTIGATION_TYPE_TO_ID[key[:-9]]  # arg name is : 'NAME_contains', pass NAME
                filters.append({"condition": "CONTAIN", "value": argToList(value), "typeId": type_id})
        if filters:
            custom_criteria["item"] = filters

        return custom_criteria

    @staticmethod
    def create_historical_investigation_payload(criteria_kvp, criteria_source, search_period):
        payload = {}
        if criteria_kvp:
            payload["criteriaKvp"] = criteria_kvp
        if criteria_source:
            payload["criteriaSource"] = criteria_source
        if search_period:
            payload["searchPeriod"] = SEARCH_PERIOD_NAME_TYPE_TO_NUM[search_period]

        return payload

    def create_historical_investigation(self, args):
        criteria_kvp = args.get("criteria_kvp", "")
        criteria_source = args.get("criteria_source", "")
        search_period = args.get("search_period", "")

        payload = self.create_historical_investigation_payload(criteria_kvp, criteria_source, search_period)

        payload["criteria"] = self.create_custom_criteria(args)

        request_data = {
            "Url": "V1/Task/CreateQuickScan",
            "TaskType": 4,  # For Endpoint Sensor, the value is always 4.
            "Payload": payload,
        }
        headers = {
            "Content-Type": "application/json;charset=utf-8",
            "Authorization": "Bearer "
            + self.create_jwt_token(http_method="POST", api_path=self.suffix, headers="", request_body=json.dumps(request_data)),
        }

        response = self._http_request("POST", self.suffix, headers=headers, data=json.dumps(request_data))
        self.validate_response(response, error_message="The historical investigation creation was unsuccessful")
        return response

    def create_result_list_payload(
        self,
        limit,
        offset,
        scan_type,
        filter_by_task_name: str = "",
        filter_by_creator_name: str = "",
        filter_by_scan_type: str = "",
        filter_by_criteria_name: str = "",
        scan_schedule_id: str = "",
    ):
        payload = {
            "pagination": {"limit": int(limit), "offset": int(offset)},
            "scanType": [SCAN_TYPE_TO_NUM[scan_type] for scan_type in argToList(scan_type)],
        }
        if scan_schedule_id:
            payload["scanScheduleId"] = scan_schedule_id

        payload_filter = []
        if filter_by_task_name:
            payload_filter.append(
                self.create_filter_entry(filter_by_task_name, "task_name", INVESTIGATION_RESULT_FILTER_TYPE_TO_NUM)
            )
        if filter_by_creator_name:
            payload_filter.append(
                self.create_filter_entry(filter_by_creator_name, "creator_name", INVESTIGATION_RESULT_FILTER_TYPE_TO_NUM)
            )
        if filter_by_scan_type:
            payload_filter.append(
                self.create_filter_entry(filter_by_scan_type, "scan_type", INVESTIGATION_RESULT_FILTER_TYPE_TO_NUM)
            )
        if filter_by_criteria_name:
            payload_filter.append(
                self.create_filter_entry(filter_by_criteria_name, "criteria_name", INVESTIGATION_RESULT_FILTER_TYPE_TO_NUM)
            )

        if payload_filter:
            payload["filter"] = payload_filter

        return payload

    def investigation_result_list(
        self,
        scan_type: str,
        limit: str = "50",
        offset: str = "0",
        filter_by_task_name: str = "",
        filter_by_creator_name: str = "",
        filter_by_scan_type: str = "",
        filter_by_criteria_name: str = "",
        scan_schedule_id: str = "",
    ):
        payload = self.create_result_list_payload(
            limit,
            offset,
            scan_type,
            filter_by_task_name,
            filter_by_creator_name,
            filter_by_scan_type,
            filter_by_criteria_name,
            scan_schedule_id,
        )

        request_data = {
            "Url": "V1/Task/ShowScanSummaryList",
            "TaskType": 4,  # For Endpoint Sensor, the value is always 4.
            "Payload": payload,
        }
        headers = {
            "Content-Type": "application/json;charset=utf-8",
            "Authorization": "Bearer "
            + self.create_jwt_token(http_method="PUT", api_path=self.suffix, headers="", request_body=json.dumps(request_data)),
        }
        response = self._http_request("PUT", self.suffix, headers=headers, data=json.dumps(request_data))
        self.validate_response(response, "The investigation result list command was unsuccessfu")

        return response


""" COMMANDS + REQUESTS FUNCTIONS """


def test_module(client: Client):
    """
    Performs basic get request to get item samples
    """
    client.udso_list()
    return "ok"


def udso_list_command(client: Client, args):
    list_type = args.get("type", "")
    content_filter = args.get("content_filter", "")

    response = client.udso_list(list_type, content_filter)
    list_data = response.get("Data", [])
    readable_output = tableToMarkdown("Apex One UDSO List", list_data)

    context = {
        "TrendMicroApex.UDSO(val.content == obj.content)": list_data,
        "TrendMicroApex.USDO(val.content == obj.content)": list_data,  # for bc reasons
    }

    return CommandResults(readable_output=readable_output, outputs=context, raw_response=response)


def udso_delete_command(client: Client, args):
    list_type = args.get("type", "")
    content = args.get("content", "")

    response = client.udso_delete(list_type, content)

    readable_output = f'### UDSO "{content}" of type "{list_type}" was deleted successfully'
    return CommandResults(readable_output=readable_output, raw_response=response)


def udso_add_command(client: Client, args):
    add_type = args.get("type")
    content = args.get("content")
    scan_action = args.get("scan_action")
    notes = args.get("notes", "")
    expiration = args.get("expiration", "")
    response = client.udso_add(add_type=add_type, content=content, scan_action=scan_action, notes=notes, expiration=expiration)

    readable_output = f'### UDSO "{content}" of type "{add_type}" was added successfully with scan action "{scan_action}"'
    return CommandResults(readable_output=readable_output, raw_response=response)


def prodagent_isolate_command(client: Client, args):
    multi_match = args.get("multi_match", "true") == "true"
    entity_id = args.get("entity_id")
    ip = args.get("ip_address")
    mac = args.get("mac_address")
    host = args.get("host_name")
    product = args.get("product")

    if not any([entity_id, ip, mac, host, product]):
        raise ValueError(
            "At least one of the following arguments must be provided: entity_id, ip_address, mac_address, host_name, product"
        )

    response = client.prodagent_isolate(
        multi_match=multi_match, entity_id=entity_id, ip_add=ip, mac_add=mac, host=host, prod=product
    )
    result_content = response.get("result_content", [])
    if result_content:
        readable_output = tableToMarkdown("Apex One ProductAgent Isolate", result_content)

    else:
        readable_output = "### No agents were affected."

    return CommandResults(
        readable_output=readable_output,
        outputs_prefix="TrendMicroApex.ProductAgent",
        outputs=result_content,
        raw_response=response,
    )


def prodagent_restore_command(client: Client, args):
    multi_match = args.get("multi_match", "true") == "true"
    entity_id = args.get("entity_id")
    ip = args.get("ip_address")
    mac = args.get("mac_address")
    host = args.get("host_name")
    product = args.get("product")

    if not any([entity_id, ip, mac, host, product]):
        raise ValueError(
            "At least one of the following arguments must be provided: entity_id, ip_address, mac_address, host_name, product"
        )

    response = client.prodagent_restore(
        multi_match=multi_match, entity_id=entity_id, ip_add=ip, mac_add=mac, host=host, prod=product
    )
    result_content = response.get("result_content", [])
    if result_content:
        readable_output = tableToMarkdown("Apex One ProductAgent Restore", result_content)
    else:
        readable_output = "### No agents were affected."

    return CommandResults(
        readable_output=readable_output,
        outputs_prefix="TrendMicroApex.ProductAgent",
        outputs=result_content,
        raw_response=response,
    )


def list_logs_command(client: Client, args):
    client.suffix = "/WebApp/api/v1/logs"
    limit = int(args.pop("limit", 50))
    response = client.logs_list(**assign_params(**args))
    parsed_logs_list = []

    if response and response.get("Data", {}).get("Logs"):
        parsed_logs_list = client.parse_cef_logs_to_dict_logs(response)[:limit]

    log_type = args.get("log_type")
    headers = ["EventName", "EventID", "CreationTime", "LogVersion", "ApplianceVersion", "ApplianceProduct", "ApplianceVendor"]
    readable_output = tableToMarkdown(
        f"Trend Micro Apex One - {log_type} Logs", parsed_logs_list, headers=headers, removeNull=True
    )

    return CommandResults(
        readable_output=readable_output, outputs_prefix="TrendMicroApex.Log", outputs=parsed_logs_list, raw_response=response
    )


def udso_file_add_command(client: Client, args):
    client.suffix = "/WebApp/api/SuspiciousObjectResource/FileUDSO"
    note = args.get("note")
    file_scan_action = args.get("file_scan_action")
    entry_id = args.get("entry_id")

    file_name, file_content_base64_string = client.get_file_name_and_base_64_content(entry_id)
    response = client.udso_add_file(file_content_base64_string, file_name, file_scan_action, note)
    readable_output = f'### The file "{file_name}" was added to the UDSO list successfully'

    return CommandResults(readable_output=readable_output, raw_response=response)


def servers_list_command(client: Client, args):
    client.suffix = "/WebApp/API/ServerResource/ProductServers"

    response = client.servers_or_agents_list(**assign_params(**args))

    for item in response.get("result_content"):  # parse comma separated str to list
        item["ip_address_list"] = item.get("ip_address_list", "").split(",")

    context = human_readable_table = []
    if response and response.get("result_content"):
        context = human_readable_table = response.get("result_content")

    headers = ["entity_id", "product", "host_name", "ip_address_list", "capabilities"]
    readable_output = tableToMarkdown(
        "Trend Micro Apex One Servers List",
        human_readable_table,
        headers,
        headerTransform=string_to_table_header,
        removeNull=True,
    )
    return CommandResults(
        readable_output=readable_output,
        outputs_prefix="TrendMicroApex.Server",
        outputs=context,
        outputs_key_field="entity_id",
        raw_response=response,
    )


def agents_list_command(client: Client, args):
    client.suffix = "/WebApp/API/AgentResource/ProductAgents"

    response = client.servers_or_agents_list(**assign_params(**args))

    for item in response.get("result_content"):  # parse comma separated str to list
        item["ip_address_list"] = item.get("ip_address_list", "").split(",")

    context = human_readable_table = []
    if response and response.get("result_content"):
        context = human_readable_table = response.get("result_content")

    readable_output = tableToMarkdown(
        "Trend Micro Apex One Agents List", human_readable_table, headerTransform=string_to_table_header, removeNull=True
    )

    return CommandResults(
        readable_output=readable_output,
        outputs_prefix="TrendMicroApex.Agent",
        outputs=context,
        outputs_key_field="entity_id",
        raw_response=response,
    )


def endpoint_sensors_list_command(client: Client, args):
    client.suffix = "/WebApp/OSCE_iES/OsceIes/ApiEntry"

    response = client.endpoint_sensors_list(**assign_params(**args))
    human_readable_table = []
    if response:
        # extract the sensor agents entities from the response
        content_list = response.get("Data", {}).get("Data", {}).get("content", {})
        for content_item in content_list:
            agent = content_item.get("content", {}).get("agentEntity", [])
            if agent:
                if agent[0].get("isolateStatus"):
                    agent["isolateStatus"] = AGENT_ISOLATION_STATUS_NUM_TO_VALUE[agent["isolateStatus"]]
                human_readable_table.append(agent[0])

    readable_output = tableToMarkdown(
        "Trend Micro Apex One Security Agents with Endpoint Sensor enabled", human_readable_table, removeNull=True
    )

    return CommandResults(
        readable_output=readable_output,
        outputs_prefix="TrendMicroApex.EndpointSensorSecurityAgent",
        outputs=human_readable_table,
        outputs_key_field="agentGuid",
        raw_response=response,
    )


def create_historical_investigation(client: Client, args):
    client.suffix = "/WebApp/OSCE_iES/OsceIes/ApiEntry"
    response = client.create_historical_investigation(args)
    context = response
    if response:
        context = response.get("Data", {}).get("Data", {})

        headers = ["taskId", "serverName", "serverGuid"]
        readable_output = tableToMarkdown(
            "The historical investigation was created successfully", context, headers=headers, removeNull=True
        )
    else:
        readable_output = ""

    return CommandResults(
        readable_output=readable_output,
        outputs_prefix="TrendMicroApex.HistoricalInvestigation",
        outputs=context,
        outputs_key_field="taskId",
        raw_response=response,
    )


def investigation_result_list_command(client: Client, args):
    client.suffix = "/WebApp/OSCE_iES/OsceIes/ApiEntry"
    response = client.investigation_result_list(**assign_params(**args))
    context = {}
    readable_output = ""
    if response:
        content_list = response.get("Data", {}).get("Data", {}).get("content", [])
        if content_list:
            results_list = content_list[0].get("content", {}).get("scanSummaryEntity")
            if results_list:
                context = results_list = client.convert_timestamps_and_scan_type_to_readable(results_list)

            headers = ["name", "scanSummaryId", "scanSummaryGuid", "submitTime", "serverGuidList", "creator"]
            readable_output = tableToMarkdown("Investigation result list:", results_list, headers=headers)

    return CommandResults(
        readable_output=readable_output,
        outputs_prefix="TrendMicroApex.InvestigationResult",
        outputs=context,
        outputs_key_field="scanSummaryGuid",
        raw_response=response,
    )


""" COMMANDS MANAGER / SWITCH PANEL """


def main():
    """GLOBALS/PARAMS"""

    params = demisto.params()

    api_key = params.get("credentials_api_token", {}).get("password") or params.get("token")
    if not api_key:
        return_error("API Key must be provided.")
    app_id = params.get("application_id")

    base_url = urljoin(params.get("url"), "")
    verify = not params.get("insecure", False)
    proxy = params.get("proxy", False)

    client = Client(base_url, api_key, app_id, verify=verify, proxy=proxy)
    command = demisto.command()
    demisto.info(f"Command being called is {command}")
    try:
        if command == "test-module":
            test_result = test_module(client)
            return_results(test_result)

        elif command in ("trendmicro-apex-udso-list", "trendmicro-apex-usdo-list"):  # For bc reasons.
            return_results(udso_list_command(client, demisto.args()))

        elif command in ("trendmicro-apex-udso-add", "trendmicro-apex-usdo-add"):  # For bc reasons
            return_results(udso_add_command(client, demisto.args()))

        elif command in ("trendmicro-apex-udso-delete", "trendmicro-apex-usdo-delete"):  # For bc reasons
            return_results(udso_delete_command(client, demisto.args()))

        elif command == "trendmicro-apex-isolate":
            return_results(prodagent_isolate_command(client, demisto.args()))

        elif command == "trendmicro-apex-restore":
            return_results(prodagent_restore_command(client, demisto.args()))

        elif command == "trendmicro-apex-list-logs":
            return_results(list_logs_command(client, demisto.args()))

        elif command == "trendmicro-apex-udso-file-add":
            return_results(udso_file_add_command(client, demisto.args()))

        elif command == "trendmicro-apex-managed-servers-list":
            return_results(servers_list_command(client, demisto.args()))

        elif command == "trendmicro-apex-security-agents-list":
            return_results(agents_list_command(client, demisto.args()))

        elif command == "trendmicro-apex-endpoint-sensors-list":
            return_results(endpoint_sensors_list_command(client, demisto.args()))

        elif command == "trendmicro-apex-historical-investigation-create":
            return_results(create_historical_investigation(client, demisto.args()))

        elif command == "trendmicro-apex-investigation-result-list":
            return_results(investigation_result_list_command(client, demisto.args()))

    except ValueError as e:
        return_error(f"Error from TrendMicro Apex One integration: {e!s}", e)


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