Covalence Managed Security
Triggers by triaged alerts from endpoint, cloud, and network security monitoring. Contains event details and easy-to-follow mitigation steps.
Endpoint · Covalence Managed Security
Details
| ID | Covalence Managed Security |
|---|---|
| Provider | Field Effect Software Inc |
| Category | Endpoint |
| From Version | 5.0.0 |
| Docker Image | demisto/python3:3.12.12.7090913 |
| Supported Modules | Agentix XSIAM |
README
Triggers by triaged alerts from endpoint, cloud, and network security monitoring. Contains event details and easy-to-follow mitigation steps.
This integration was integrated and tested with version 1.1.10 of Covalence Managed Security.
Configure Covalence Managed Security in Cortex
| Parameter | Description | Required |
|---|---|---|
| Credentials | True | |
| Password | True | |
| Use system proxy settings | False | |
| First run time range | When fetching incidents for the first time, this parameter specifies in days how far the integration looks for incidents. For instance if set to “2”, it will pull all alerts in Covalence for the last 2 days and will create corresponding incidents. | False |
| Incident type | False | |
| Fetch incidents | False | |
| First fetch timestamp (<number> <time unit>, e.g., 12 hours, 7 days) | False | |
| Fetch Limit | The maximum number of incidents to fetch | False |
| Broker Server URL | Broker Server URL (Optional). Required to use Broker commands. | 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.
cov-mgsec-get-aro
Query FES Portal for ARO.
Base Command
cov-mgsec-get-aro
Input
| Argument Name | Description | Required |
|---|---|---|
| details | if details=true, will return the complete response from Covalence API. | Optional |
| query | Portal query, for example: “resolution=Unresolved&type=Recommendation” Available Keys to filter on: - id; eg: “id=<ARO_id> - status; eg: “status=In Triage” or “status=Open” or “status=Closed” - resolution; eg: “resolution=Unresolved” or “resolution=Resolved” or “resolution=Help Requested” or “resolution=Dismissed” - type; eg: “type=Action” or “type=Recommendation” or “type=Observation” - org; eg: “org=<organization_name>“ - since; eg: “since=2021-01-31 14:00:00” - until; eg: “until=2021-01-31 14:00:00”. |
Required |
Context Output
| Path | Type | Description |
|---|---|---|
| FESPortal.Aro.ID | String | ID. |
| FESPortal.Aro.alert_key | String | Alert_key. |
| FESPortal.Aro.analyst_notes | String | Analyst_notes. |
| FESPortal.Aro.count | Number | Count. |
| FESPortal.Aro.creation_time | Date | Creation_time. |
| FESPortal.Aro.details | String | Details. |
| FESPortal.Aro.details_markdown | String | Details_markdown. |
| FESPortal.Aro.display_url | String | Display_url. |
| FESPortal.Aro.external_bug_id | String | External_bug_id. |
| FESPortal.Aro.last_updated_time | Date | Last_updated_time. |
| FESPortal.Aro.notes | String | Notes. |
| FESPortal.Aro.organization.ID | String | ID. |
| FESPortal.Aro.organization.email | String | Email. |
| FESPortal.Aro.organization.name | String | Name. |
| FESPortal.Aro.resolution | String | Resolution. |
| FESPortal.Aro.serial_id | String | Serial_id. |
| FESPortal.Aro.severity | String | Severity. |
| FESPortal.Aro.status | String | Status. |
| FESPortal.Aro.steps.ID | String | ID. |
| FESPortal.Aro.steps.completed | Boolean | Completed. |
| FESPortal.Aro.steps.label | String | Label. |
| FESPortal.Aro.steps.last_updated_time | Date | Last_updated_time. |
| FESPortal.Aro.template_id | String | Template_id. |
| FESPortal.Aro.title | String | Title. |
| FESPortal.Aro.triage_id | String | Triage_id. |
| FESPortal.Aro.type | String | Type. |
Command example
!cov-mgsec-get-aro query="since=2023-11-30 18:00:00"
Context Example
{
"FESPortal": {
"ARO": [
{
"organization": {
"ID": "9d4297ea-089e-42bd-884d-51744e31a471",
"email": "foo@bar.com",
"name": "Acme"
},
"resolution": "Unresolved",
"severity": "Critical",
"status": "Open",
"title": "test2",
"type": "Action"
},
{
"organization": {
"ID": "e0e04c8b-d50c-4379-bfd6-5e0f2b1037cd",
"email": "foo@bar.com",
"name": "Capsule Corp"
},
"resolution": "Unresolved",
"severity": "High",
"status": "Open",
"title": "Vulnerable Software Detected",
"type": "Recommendation"
}
]
}
}
Human Readable Output
AROs
Organization Resolution Severity Status Title Type ID: 9d4297ea-089e-42bd-884d-51744e31a471
email: foo@bar.com
name: AcmeUnresolved Critical Open test2 Action ID: e0e04c8b-d50c-4379-bfd6-5e0f2b1037cd
email: foo@bar.com
name: Capsule CorpUnresolved High Open Vulnerable Software Detected Recommendation
cov-mgsec-list-org
List organizations.
Base Command
cov-mgsec-list-org
Input
There are no input arguments for this command.
Context Output
| Path | Type | Description |
|---|---|---|
| FESPortal.Org.ID | String | ID. |
| FESPortal.Org.email | String | Email. |
| FESPortal.Org.email_aro_details | Boolean | Email_aro_details. |
| FESPortal.Org.name | String | Name. |
Command example
#### Context Example
```json
{
"FESPortal": {
"Org": [
{
"ID": "9d4297ea-089e-42bd-884d-51744e31a471",
"email": "foo@bar.com",
"email_aro_details": false,
"name": "Acme"
},
{
"ID": "e0e04c8b-d50c-4379-bfd6-5e0f2b1037cd",
"email": "foo@bar.com",
"email_aro_details": false,
"name": "Capsule Corp"
}
]
}
}
Human Readable Output
Organizations
Id Email Aro Details Name 9d4297ea-089e-42bd-884d-51744e31a471 foo@bar.com false Acme e0e04c8b-d50c-4379-bfd6-5e0f2b1037cd foo@bar.com false Capsule Corp
cov-mgsec-transition-aro
Transition an ARO.
Base Command
cov-mgsec-transition-aro
Input
| Argument Name | Description | Required |
|---|---|---|
| aro_id | This ARO ID to transition. | Required |
| resolution | Resolution to transition the ARO to. Possible values are: Unresolved, Help Requested, Resolved, Dismissed. | Required |
| comment | Optional comment to leave on the ARO. | Optional |
| is_comment_sensitive | Optionally mark the comment as sensitive. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| FESPortal.Aro.ID | String | ID. |
| FESPortal.Aro.alert_key | String | Alert_key. |
| FESPortal.Aro.analyst_notes | String | Analyst_notes. |
| FESPortal.Aro.count | Number | Count. |
| FESPortal.Aro.creation_time | Date | Creation_time. |
| FESPortal.Aro.details | String | Details. |
| FESPortal.Aro.details_markdown | String | Details_markdown. |
| FESPortal.Aro.display_url | String | Display_url. |
| FESPortal.Aro.external_bug_id | String | External_bug_id. |
| FESPortal.Aro.last_updated_time | Date | Last_updated_time. |
| FESPortal.Aro.notes | String | Notes. |
| FESPortal.Aro.organization.ID | String | ID. |
| FESPortal.Aro.organization.email | String | Email. |
| FESPortal.Aro.organization.name | String | Name. |
| FESPortal.Aro.resolution | String | Resolution. |
| FESPortal.Aro.serial_id | String | Serial_id. |
| FESPortal.Aro.severity | String | Severity. |
| FESPortal.Aro.status | String | Status. |
| FESPortal.Aro.steps.ID | String | ID. |
| FESPortal.Aro.steps.completed | Boolean | Completed. |
| FESPortal.Aro.steps.label | String | Label. |
| FESPortal.Aro.steps.last_updated_time | Date | Last_updated_time. |
| FESPortal.Aro.template_id | String | Template_id. |
| FESPortal.Aro.title | String | Title. |
| FESPortal.Aro.triage_id | String | Triage_id. |
| FESPortal.Aro.type | String | Type. |
cov-mgsec-transition-aro
Transition an ARO.
Base Command
cov-mgsec-transition-aro
Input
| Argument Name | Description | Required |
|---|---|---|
| aro_id | This ARO ID to transition. | Required |
| resolution | Resolution to transition the ARO to. Options include: Unresolved, Help Requested, Resolved, or Dismissed. | Required |
| comment | Optional comment to leave on the ARO. | Optional |
| is_comment_sensitive | Optionally mark the comment as sensitive. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| FESPortal.Aro.ID | String | ID. |
| FESPortal.Aro.alert_key | String | Alert_key. |
| FESPortal.Aro.analyst_notes | String | Analyst_notes. |
| FESPortal.Aro.count | Number | Count. |
| FESPortal.Aro.creation_time | Date | Creation_time. |
| FESPortal.Aro.details | String | Details. |
| FESPortal.Aro.details_markdown | String | Details_markdown. |
| FESPortal.Aro.display_url | String | Display_url. |
| FESPortal.Aro.external_bug_id | String | External_bug_id. |
| FESPortal.Aro.last_updated_time | Date | Last_updated_time. |
| FESPortal.Aro.notes | String | Notes. |
| FESPortal.Aro.organization.ID | String | ID. |
| FESPortal.Aro.organization.email | String | Email. |
| FESPortal.Aro.organization.name | String | Name. |
| FESPortal.Aro.resolution | String | Resolution. |
| FESPortal.Aro.serial_id | String | Serial_id. |
| FESPortal.Aro.severity | String | Severity. |
| FESPortal.Aro.status | String | Status. |
| FESPortal.Aro.steps.ID | String | ID. |
| FESPortal.Aro.steps.completed | Boolean | Completed. |
| FESPortal.Aro.steps.label | String | Label. |
| FESPortal.Aro.steps.last_updated_time | Date | Last_updated_time. |
| FESPortal.Aro.template_id | String | Template_id. |
| FESPortal.Aro.title | String | Title. |
| FESPortal.Aro.triage_id | String | Triage_id. |
| FESPortal.Aro.type | String | Type. |
Command example
!cov-mgsec-transition-aro aro_id="7ea9b17d-7529-4b17-b0e7-92334d6c674b" resolution="Resolved" comment="Risk mitigated."
Context Example
{
"FESPortal": {
"Org": {
"ID": "7ea9b17d-7529-4b17-b0e7-92334d6c674b",
"alert_key": "test_alert_key",
"attachments": [],
"count": 1,
"creation_time": "2023-08-16 19:48:02",
"data": null,
"details": "ARO Details",
"details_markdown": null,
"display_url": "test_url",
"external_ticket": null,
"frameworks": [],
"insights": {},
"last_updated_time": "2023-11-30 19:01:59",
"organization": {
"ID": "test_ID",
"email": null,
"name": "test_org_id"
},
"references": [],
"resolution": "Resolved",
"resolution_duration_seconds": 9155637,
"resolution_time": "2023-11-30 19:01:59",
"serial_id": "15",
"severity": "Low",
"status": "Open",
"steps": [
{
"ID": "test_id",
"completed": true,
"label": "test_resolution_step",
"last_updated_time": "2023-10-24 20:53:45"
}
],
"template_id": null,
"title": "test_aro_title",
"triage_id": null,
"type": "Observation"
}
}
}
Human Readable Output
ARO
Id Alert Key Count Creation Time Details Display Url Last Updated Time Organization Resolution Resolution Duration Seconds Resolution Time Serial Id Severity Status Steps Title Type 7ea9b17d-7529-4b17-b0e7-92334d6c674b test_alert_key 1 2023-08-16 19:48:02 ARO Details test_url 2023-11-30 19:01:59 ID: test_ID
email: null
name: test_org_idResolved 9155637 2023-11-30 19:01:59 15 Low Open {‘ID’: ‘test_id’, ‘completed’: True, ‘label’: ‘test_resolution_step’, ‘last_updated_time’: ‘2023-10-24 20:53:45’} test_aro_title Observation cov-mgsec-broker-cloud-action-by-aro
Broker - Cloud Action By ARO.
Base Command
cov-mgsec-broker-cloud-action-by-aro
Input
| Argument Name | Description | Required |
|---|---|---|
| action_type | Action to perform. Possible values are: DISABLE_USER, ENABLE_USER, REVOKE_SESSIONS. | Required |
| aro_id | ARO ID (eg. “00000000-1111-2222-3333-444444444444”). | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| FESBroker.action_id | String | Action ID |
| FESBroker.action_type | String | Action Type |
| FESBroker.action_params | Unknown | Action Parameters |
| FESBroker.created_time | String | Created Time |
| FESBroker.status | String | Status |
| FESBroker.result | String | Result |
Command example
!cov-mgsec-broker-cloud-action-by-aro action_type=DISABLE_USER aro_id=00000000-1111-2222-3333-444444444444
Context Example
{
"FESBroker": {
"Action": {
"action_id": "00000000-1111-2222-3333-444444444444",
"action_params": {
"user": "azure credential configuration endpoint service"
},
"action_type": "disable_user",
"created_time": "2024-02-22T01:27:04.344179Z",
"result": "SUCCESS",
"status": "COMPLETE"
}
}
}
Human Readable Output
Command Result
action_id action_params action_type created_time result status 00000000-1111-2222-3333-444444444444 user: azure credential configuration endpoint service disable_user 2024-02-22T01:27:04.344179Z SUCCESS COMPLETE
cov-mgsec-broker-endpoint-action-by-aro
Broker - Endpoint Action By ARO.
Base Command
cov-mgsec-broker-endpoint-action-by-aro
Input
| Argument Name | Description | Required |
|---|---|---|
| action_type | Action to send to Host. Possible values are: ISOLATE, UNISOLATE, SHUTDOWN, RESTART, DEFENDER_QUICK_SCAN, DEFENDER_FULL_SCAN, DEFENDER_SIGNATURE_UPDATE. | Required |
| aro_id | ARO ID (eg. “00000000-1111-2222-3333-444444444444”). | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| FESBroker.host_identifier | String | Host Identifier |
| FESBroker.agent_uuid | String | Agent UUID |
| FESBroker.covalence_appliance | String | Covalence Appliance ID |
| FESBroker.task_id | Number | Endpoint Action Task ID |
Command example
!cov-mgsec-broker-endpoint-action-by-aro action_type=DEFENDER_QUICK_SCAN aro_id=00000000-1111-2222-3333-444444444444
Context Example
{
"FESBroker": {
"Action": {
"agent_uuid": "00000000-1111-2222-3333-444444444444",
"covalence_appliance": "2000-001-XX-0",
"host_identifier": "00000000-1111-2222-3333-444444444444",
"task_id": 26876
}
}
}
Human Readable Output
Command Result - Success
agent_uuid covalence_appliance host_identifier task_id 00000000-1111-2222-3333-444444444444 2000-001-XX-0 00000000-1111-2222-3333-444444444444 26876
cov-mgsec-broker-ping
Broker - Ping.
Base Command
cov-mgsec-broker-ping
Input
| Argument Name | Description | Required |
| — | — | — |
Context Output
| Path | Type | Description |
|---|---|---|
| FESBroker.APIStatus | String | API Status |
Command example
#### Context Example
```json
{
"FESBroker": {
"APIStatus": "pong"
}
}
Human Readable Output
Success
cov-mgsec-broker-endpoint-action-by-host
Broker - Endpoint Action By Host.
Base Command
cov-mgsec-broker-endpoint-action-by-host
Input
| Argument Name | Description | Required |
|---|---|---|
| action_type | Action to send to Host. Possible values are: ISOLATE, UNISOLATE, SHUTDOWN, RESTART, DEFENDER_QUICK_SCAN, DEFENDER_FULL_SCAN, DEFENDER_SIGNATURE_UPDATE. | Required |
| org_id | Organization ID (eg. “00000000-1111-2222-3333-444444444444”). | Required |
| host_identifier | Hostname. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| FESBroker.host_identifier | String | Host Identifier |
| FESBroker.agent_uuid | String | Agent UUID |
| FESBroker.covalence_appliance | String | Covalence Appliance ID |
| FESBroker.task_id | Number | Endpoint Action Task ID |
Command example
!cov-mgsec-broker-endpoint-action-by-host action_type=DEFENDER_QUICK_SCAN host_identifier=test-hostname org_id=00000000-1111-2222-3333-444444444444
Context Example
{
"FESBroker": {
"Action": {
"agent_uuid": "00000000-1111-2222-3333-444444444444",
"covalence_appliance": "2000-001-XX-0",
"host_identifier": "test-hostname",
"task_id": 24773
}
}
}
Human Readable Output
Command Result - Success
agent_uuid covalence_appliance host_identifier task_id 00000000-1111-2222-3333-444444444444 2000-001-XX-0 test-hostname 24773
cov-mgsec-broker-list-org
Broker - List organizations.
Base Command
cov-mgsec-broker-list-org
Input
| Argument Name | Description | Required |
| — | — | — |
Context Output
| Path | Type | Description |
|---|---|---|
| FESBroker.ID | String | Organization ID |
| FESBroker.name | String | Organization Name |
| FESBroker.client_id | String | Client ID |
Command example
#### Context Example
```json
{
"FESBroker": {
"Org": [
{
"ID": "00000000-1111-2222-3333-444444444444",
"client_id": "2000-001-XX-0",
"name": "Test Company"
}
]
}
}
Human Readable Output
Organizations
ID client_id name 00000000-1111-2222-3333-444444444444 2024-1384-SAN 110 Sand Company
cov-mgsec-comment-aro
Comment on an ARO.
Base Command
cov-mgsec-comment-aro
Input
| Argument Name | Description | Required |
|---|---|---|
| aro_id | This ARO ID to transition. | Required |
| comment | Comment to leave on the ARO. | Required |
| is_comment_sensitive | Optionally mark the comment as sensitive. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| FESPortal.Aro.ID | String | ID. |
| FESPortal.Aro.acknowledged | Boolean | Acknowledged |
| FESPortal.Aro.acknowledged_by.ID | String | Acknowledged By ID |
| FESPortal.Aro.acknowledged_by.avatar_file_url | String | Acknowledged By |
| FESPortal.Aro.acknowledged_by.email | String | Acknowledged By Email |
| FESPortal.Aro.acknowledged_by.first_name | String | Acknowledged By First Name |
| FESPortal.Aro.acknowledged_by.last_name | String | Acknowledged By Last Name |
| FESPortal.Aro.acknowledged_time | Date | Acknowledged Time |
| FESPortal.Aro.aro_id | String | ARO ID |
| FESPortal.Aro.author.ID | String | Author ID |
| FESPortal.Aro.author.avatar_file_url | String | Author Avatar File URL |
| FESPortal.Aro.author.email | String | Author Email |
| FESPortal.Aro.author.first_name | String | Author First Name |
| FESPortal.Aro.author.last_name | String | Author Last Name |
| FESPortal.Aro.author_organization.ID | String | Author Organization ID |
| FESPortal.Aro.author_organization.email | String | Author Organization Email |
| FESPortal.Aro.author_organization.name | String | Author Organization Name |
| FESPortal.Aro.author_organization_type | String | Author Organization Type |
| FESPortal.Aro.available_only_to_organization_id | String | ARO Comment Available Only to Organization ID |
| FESPortal.Aro.available_only_to_provider_id | String | ARO Comment Available Only to Provider ID |
| FESPortal.Aro.created_time | Date | ARO Created Time |
| FESPortal.Aro.id | String | ARO Comment ID |
| FESPortal.Aro.last_updated_time | Date | ARO Comment Last Updated Time |
| FESPortal.Aro.sensitive | Boolean | ARO Comment Sensitive |
| FESPortal.Aro.source | String | ARO Comment Source |
| FESPortal.Aro.text | String | ARO Comment Text |
| FESPortal.Aro.type | String | ARO Comment Type |
| FESPortal.Aro.visible_to.ID | String | ARO Comment Visible to ID |
| FESPortal.Aro.visible_to.email | String | ARO Comment Visible to Email |
| FESPortal.Aro.visible_to.name | String | ARO Comment Visible to Name |
Command example
!cov-mgsec-comment-aro aro_id="b25e461e-75e9-415b-a631-6d0f4516f33a" comment="Risk mitigated."
Context Example
{
"FESPortal": {
"Org": {
"acknowledged": true,
"acknowledged_by": {
"ID": "abcdefghijklmnopqrstuvwxyzabd1",
"avatar_file_url": null,
"email": "foo@bar.com",
"first_name": "John",
"last_name": "Smith"
},
"acknowledged_time": "2024-04-12 17:01:25",
"aro_id": "b25e461e-75e9-415b-a631-6d0f4516f33a",
"author": {
"ID": "abcdefghijklmnopqrstuvwxyzabd1",
"avatar_file_url": null,
"email": "foo@bar.com",
"first_name": "John",
"last_name": "Smith"
},
"author_organization": {
"ID": "00000000-1111-2222-3333-444444444444",
"email": "foo@bar.com",
"name": "Field Effect"
},
"author_organization_type": "Field Effect",
"available_only_to_organization_id": null,
"available_only_to_provider_id": null,
"created_time": "2024-04-12 17:01:25",
"id": "b14a53a4-23ac-488d-b992-dbc1d5ef5361",
"last_updated_time": "2024-04-12 17:01:25",
"sensitive": false,
"source": "Portal",
"text": "Risk mitigated.",
"type": "Comment",
"visible_to": [
{
"ID": "00000000-1111-2222-3333-444444444444",
"email": null,
"name": "Tradecraft Test & Development (Do Not Delete)"
},
{
"ID": "00000000-1111-2222-3333-444444444444",
"email": "foo@bar.com",
"name": "Field Effect"
}
]
}
}
}
Human Readable Output
ARO
Acknowledged Acknowledged By Acknowledged Time Aro Id Author Author Organization Author Organization Type Created Time Id Last Updated Time Sensitive Source Text Type Visible To true ID: abcdefghijklmnopqrstuvwxyzabd1
avatar_file_url: null
email: foo@bar.com
first_name: John
last_name: Smith2024-04-12 17:01:25 b25e461e-75e9-415b-a631-6d0f4516f33a ID: abcdefghijklmnopqrstuvwxyzabd1
avatar_file_url: null
email: foo@bar.com
first_name: John
last_name: SmithID: 00000000-1111-2222-3333-444444444444
email: foo@bar.com
name: Field EffectField Effect 2024-04-12 17:01:25 b14a53a4-23ac-488d-b992-dbc1d5ef5361 2024-04-12 17:01:25 false Portal Risk mitigated. Comment {‘ID’: ‘00000000-1111-2222-3333-444444444444’, ‘email’: None, ‘name’: ‘Tradecraft Test & Development (Do Not Delete)’},
{‘ID’: ‘00000000-1111-2222-3333-444444444444’, ‘email’: ‘foo@bar.com’, ‘name’: ‘Field Effect’}cov-mgsec-list-escalation-contacts
Get the escalation contact list for a given organization.
Base Command
cov-mgsec-list-escalation-contacts
Input
| Argument Name | Description | Required |
|---|---|---|
| org_id | Organization ID (eg. “00000000-1111-2222-3333-444444444444”). | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| FESPortal.Org.priority | String | Priority. |
| FESPortal.Org.first_name | String | First Name. |
| FESPortal.Org.last_name | String | Last Name. |
| FESPortal.Org.job_title | String | Job Title. |
| FESPortal.Org.phone_number | String | Phone Number. |
| FESPortal.Org.secondary_phone | String | Secondary Phone Number. |
| FESPortal.Org.email | String | Email. |
| FESPortal.Org.notes | String | Notes. |
Command example
!cov-mgsec-list-escalation-contacts org_id=cb7e26b7-9d62-4cc7-acf1-4264327ac552
Context Example
{
"FESPortal": {
"Org": [
{
"email": "test-email@fieldeffect.com",
"first_name": "John",
"job_title": null,
"last_name": "Doe",
"notes": "test notes",
"phone_number": null,
"priority": 1,
"secondary_phone": null
},
{
"email": "test-user2@fieldeffect.com",
"first_name": "Testing1",
"job_title": null,
"last_name": "User",
"notes": "test-note",
"phone_number": null,
"priority": 2,
"secondary_phone": null
},
{
"email": "test-user-4@fieldeffect.com",
"first_name": "Bob",
"job_title": "QA",
"last_name": "Tester",
"notes": "123\n456\n789",
"phone_number": "+15550199999",
"priority": 3,
"secondary_phone": "+15069999999"
},
{
"email": "test5@example.om",
"first_name": "Smith",
"job_title": "UI Dev",
"last_name": "fes",
"notes": null,
"phone_number": "+16135445544",
"priority": 4,
"secondary_phone": "+16135554548"
},
{
"email": "test-user-44@fieldeffect.com",
"first_name": "John",
"job_title": "Head of UI Development",
"last_name": "Doe",
"notes": null,
"phone_number": "+16135554545",
"priority": 5,
"secondary_phone": null
}
]
}
}
Human Readable Output
Escalation Contacts
Priority First Name Last Name Job Title Phone Number Secondary Phone Notes 1 John Doe test-email@fieldeffect.com some other test note 2 Testing1 User test-user2@fieldeffect.com test-note 3 Bob Tester QA +15550199999 +15069999999 test-user-4@fieldeffect.com 4 Smith fes UI Dev +16135445544 +16135554548 test5@example.om 5 John Doe Head of UI Development +16135554545 test-user-44@fieldeffect.com
cov-mgsec-list-key-contacts
Get the key contact list for a given organization.
Base Command
cov-mgsec-list-key-contacts
Input
| Argument Name | Description | Required |
|---|---|---|
| org_id | Organization ID (eg. “00000000-1111-2222-3333-444444444444”). | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| FESPortal.Org.first_name | String | First Name. |
| FESPortal.Org.last_name | String | Last Name. |
| FESPortal.Org.phone_number | String | Phone Number. |
| FESPortal.Org.email | String | Email. |
| FESPortal.Org.type | String | Contact Type. |
Command example
!cov-mgsec-list-key-contacts org_id=cb7e26b7-9d62-4cc7-acf1-4264327ac552
Context Example
{
"FESPortal": {
"Org": [
{
"email": "test-email@fieldeffect.com",
"first_name": "John",
"last_name": "Doe",
"phone_number": null,
"type": "Administrative"
},
{
"email": "test-email@fieldeffect.com",
"first_name": "John",
"last_name": "Doe",
"phone_number": "+19022223333",
"type": "Technical - Primary"
},
{
"email": "test5@example.om",
"first_name": "Smith",
"last_name": "fes",
"phone_number": "+16135445544",
"type": "Technical - Secondary"
}
]
}
}
Human Readable Output
Key Contacts
First Name Last Name Phone Number Type John Doe test-email@fieldeffect.com Administrative John Doe +19022223333 test-email@fieldeffect.com Technical - Primary Smith fes +16135445544 test5@example.om Technical - Secondary
cov-mgsec-list-language
Get the default language for a given organization.
Base Command
cov-mgsec-list-language
Input
| Argument Name | Description | Required |
|---|---|---|
| org_id | Organization ID (eg. “00000000-1111-2222-3333-444444444444”). | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| FESPortal.Org.default_language | String | Default Language. |
Command example
!cov-mgsec-list-language org_id=cb7e26b7-9d62-4cc7-acf1-4264327ac552
Context Example
{
"FESPortal": {
"Org": {
"default_language": "en-CA"
}
}
}
Human Readable Output
Default Organization Language
Default Language en-CA
Configuration parameters
credentials— Credentials (required)proxy— Use system proxy settingsfirst_run_time_range— First run time rangeincidentType— Incident typeincidentFetchInterval— Incidents Fetch IntervalisFetch— Fetch incidentsfirst_fetch— First fetch timestamp (<number> <time unit>, e.g., 12 hours, 7 days)max_fetch— Fetch Limitbroker_url— Broker Server URL
Commands (12)
-
cov-mgsec-broker-cloud-action-by-aroBroker - Cloud Action By ARO.
-
cov-mgsec-broker-endpoint-action-by-aroBroker - Endpoint Action By ARO.
-
cov-mgsec-broker-endpoint-action-by-hostBroker - Endpoint Action By Host.
-
cov-mgsec-broker-list-orgBroker - List organizations.
-
cov-mgsec-broker-pingBroker - Test connectivity to the Broker service.
-
cov-mgsec-comment-aroComment on an ARO.
-
cov-mgsec-get-aroQuery FES Portal for ARO.
-
cov-mgsec-list-escalation-contactsGet the escalation contact list for a given organization.
-
cov-mgsec-list-key-contactsGet the key contact list for a given organization.
-
cov-mgsec-list-languageGet the default language for a given organization.
-
cov-mgsec-list-orgList organizations.
-
cov-mgsec-transition-aroTransition an ARO.
import json import os import traceback from dataclasses import dataclass from datetime import datetime, timedelta from enum import Enum from typing import Literal import demistomock as demisto # noqa: F401 import requests import urllib3 from CommonServerPython import * # noqa: F401 from requests import HTTPError, Response DATE_FORMAT = "%Y-%m-%d %H:%M:%S" EMAIL = demisto.params().get("credentials")["identifier"] API_KEY = demisto.params().get("credentials")["password"] FIRST_RUN_TIME_RANGE = int(demisto.params().get("first_run_time_range").strip()) PROXY = demisto.params().get("proxy") if not demisto.params().get("proxy", False): # Remove proxy environment variables if they exist for proxy_var in ["HTTP_PROXY", "HTTPS_PROXY", "http_proxy", "https_proxy"]: os.environ.pop(proxy_var, None) class EndpointActionType(str, Enum): ISOLATE = "isolate" UNISOLATE = "unisolate" SHUTDOWN = "shutdown" RESTART = "restart" DEFENDER_QUICK_SCAN = "defender_quick_scan" DEFENDER_FULL_SCAN = "defender_full_scan" DEFENDER_SIGNATURE_UPDATE = "defender_signature_update" class CloudActionType(str, Enum): DISABLE_USER = "disable_user" ENABLE_USER = "enable_user" REVOKE_SESSIONS = "revoke_sessions" @dataclass class OrganizationResponse: # pylint: disable=invalid-name ID: str name: str client_id: str @dataclass class ActionByHostResponse: host_identifier: str agent_uuid: str covalence_appliance: str task_id: int | str @dataclass class ActionByCloudAccountResponse: action_id: str action_type: Literal["disable_user", "enable_user", "revoke_sessions"] action_params: dict created_time: str status: Literal["QUEUED", "COMPLETE"] result: Literal["SUCCESS", "FAILED", "PENDING"] class Portal: def __init__(self, bearer=None, portal_url="https://services.fieldeffect.net/v1", provider=None, verbose=False): self.auth = None self.portal_url = portal_url self.verbose = verbose self.provider_name = provider if bearer == "gan ceann": self.scheme = self.AuthScheme.FES # Headless login for those cursed with a GUI elif bearer: self.scheme = self.AuthScheme.BEARER self.auth = {"token": bearer, "expires": datetime.now() + timedelta(days=10 * 365), "refresh": None} self.provider_id = self.get_provider_id() else: raise ValueError("Bearer is missing") class AuthScheme: FES = "FieldEffectAuth" BEARER = "Bearer" KEY = "FieldEffectKey" def try_saved_token(self, token): # Return True if this token works, also save this token as the token # Return False if this token doesn't work and user will need to auth try: self.auth = token if self.provider_name: self.provider_id = self.find_provider(self.provider_name) else: self.provider_id = self.get_provider_id() return True except requests.exceptions.HTTPError: self.auth = None return False def paginated_get( self, uri: str, page_size: int = 500, limit: Optional[int] = None, query: Optional[Dict[str, Any]] = None, **kwargs, ): results = [] query = query or {} # If a limit is passed into the query dict then use it as long as it's an int and another limit # hasn't already been provided as a param if "limit" in query and isinstance(query["limit"], int) and limit is None: # popping it out to check if it's < 0 in a common check with a param provided limit limit = query.pop("limit") # if a negative limit is provided then ignore it if isinstance(limit, int) and limit < 0: limit = None # On the initial query if the limit is smaller than the page size, the page size needs to be reduced to only hit the limit if isinstance(limit, int) and limit < page_size: query["limit"] = limit else: query["limit"] = page_size r = self.get(uri, query=query, **kwargs).json() results.extend(r["items"]) while True: results_length = len(results) # break if the limit or total number of results has been exceeded if (isinstance(limit, int) and results_length >= limit) or results_length >= r["total"]: break # If there is a limit and the results_length is >= the limit size it would have broken above. # So we know here that results_length is < limit if a limit has been provided. # If the limit is smaller than the page size then just get up to the limit. if isinstance(limit, int) and (limit - results_length) < page_size: query["limit"] = limit - results_length query["page"] = r["page"] + 1 r = self.get(uri, query=query, **kwargs).json() results.extend(r["items"]) return results def get(self, uri, query=None, headers=None, remove_subdomain=False, **kwargs): return self._request(uri, method="GET", query=query, headers=headers, remove_subdomain=remove_subdomain, **kwargs) def post(self, uri, query=None, headers=None, remove_subdomain=False, **kwargs): return self._request(uri, method="POST", query=query, headers=headers, remove_subdomain=remove_subdomain, **kwargs) def _request( self, uri, method="GET", query=None, json=None, data=None, files=None, headers=None, remove_subdomain=False, **kwargs ): all_headers = {"Content-Type": "application/json"} if json is not None else {} if headers is not None: all_headers.update(headers) if self.auth: auth = "{} {}".format(self.scheme, self.auth["token"]) all_headers.update({"Authorization": auth}) url = f"{self.portal_url}/{uri if len(kwargs) == 0 else uri.format(**kwargs)}" if remove_subdomain: url = url.replace("services.", "") if self.verbose: sys.stdout.write(f"{method} {url} ") if method == "GET": r = requests.get(url, headers=all_headers, params=query) elif method == "POST": r = requests.post(url, headers=all_headers, json=json, data=data, params=query, files=files) elif method == "PUT": r = requests.put(url, headers=all_headers, json=json, data=data, params=query, files=files) elif method == "DELETE": r = requests.delete(url, headers=all_headers, params=query) else: raise AssertionError(f"Unsupported HTTP method: {method}") if self.verbose: sys.stdout.write(str(r.status_code) + "\n") if r.status_code >= 400: raise HTTPError(r.text) return r def get_provider_id(self): r = self.get("my_providers", auth=self.auth) if not r.json(): raise ValueError(f"Account {EMAIL} is not part of any provider") return r.json()[0]["ID"] def find_provider(self, provider): r = self.get("providers", auth=self.auth) providers = r.json() for prov in providers: if provider == prov["name"] or provider == prov["ID"]: return prov["ID"] return None def get_organizations(self): r = self.get("my_providers/{id}/organizations", auth=self.auth, id=self.provider_id) return r.json() def find_organizations(self, org): r = self.get("my_providers/{id}/organizations", auth=self.auth, id=self.provider_id) orgs = r.json() matches = [] for o in orgs: if org.lower() in o["name"].lower(): matches.append(o) return matches def get_aros(self, **kwargs): aros = [] if "query" not in kwargs: kwargs["query"] = {} kwargs["query"]["limit"] = 500 r = self.get("providers/{id}/aros", auth=self.auth, id=self.provider_id, **kwargs).json() aros.extend(r["items"]) while len(aros) < r["total"]: kwargs["query"]["page"] = r["page"] + 1 r = self.get("providers/{id}/aros", auth=self.auth, id=self.provider_id, **kwargs).json() aros.extend(r["items"]) return aros def get_active_response_profile(self, org_id): r = self.get("my_organizations/{org_id}", auth=self.auth, org_id=org_id) org_details = r.json() return org_details.get("active_response_profile", None) def transition_aro(self, aro_id, resolution, comment="", is_comment_sensitive=False): request = {"status": "Open", "resolution": resolution} if comment: request["comment"] = {"text": comment, "sensitive": is_comment_sensitive} r = self.post("aros/{aro_id}/transition", aro_id=aro_id, json=request) return r.json() def comment_aro(self, aro_id, comment="", is_comment_sensitive=False): request = {"aro_id": aro_id, "sensitive": is_comment_sensitive, "text": comment} r = self.post("aro_comments", json=request) return r.json() def list_escalation_contacts(self, org_id: str | None = None) -> List[Dict[str, Any]]: """ Get the escalation contact list for a given organization. :param org_id: Org ID. :return: List of escalation contacts. """ query = {} if org_id: query["organization_id"] = org_id return self.paginated_get("escalation_contact_lists", query=query) def list_organization_contacts(self, org_id: str) -> List[Dict[str, Any]]: """ Get the organization contact list for a given organization :param org_id: Org ID. :return: List of organization contacts. """ r = self.get("organizations/{org_id}", auth=self.auth, org_id=org_id) org_details = r.json() return org_details.get("contacts", None) def list_organization_language(self, org_id: str) -> Dict[str, Any]: """ Get the default language for a given organization :param org_id: Org ID. :return: Default Language """ r = self.get("organizations/{org_id}", auth=self.auth, org_id=org_id) org_details = r.json() locale = org_details.get("profile", {}).get("default_locale", None) return {"default_language": locale} class BrokerClient: def __init__(self, host: str, verify_ssl: bool = False, api_key: str | None = None, timeout: int = 60): self.host = host[:-1] if host.endswith("/") else host self.session = requests.Session() self.verify_ssl = verify_ssl self.api_key = api_key self.timeout = timeout if not verify_ssl: urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) @property def api_key(self) -> str | None: return self._api_key @api_key.setter def api_key(self, value: str | None) -> None: if isinstance(value, str): if not value.startswith("Bearer "): value = f"Bearer {value}" self.session.headers["Authorization"] = value self._api_key = value def ping(self) -> str: return self._request(method="GET", path="/ping", check_authentication=False).text def organizations(self, to_dataclass: bool = False) -> list[dict] | list[OrganizationResponse]: response: list[dict] = self._request(method="GET", path="/organizations").json() if to_dataclass is True: return [OrganizationResponse(**entry) for entry in response] return response def endpoint_action_by_host( self, action_type: EndpointActionType, org_id: str, host_identifier: str, to_dataclass: bool = False, ) -> dict | ActionByHostResponse: response: dict = self._request( method="POST", path=f"/endpoint/host/{action_type.value}", json={"org_id": org_id, "host_identifier": host_identifier}, ).json() if to_dataclass is True: return ActionByHostResponse(**response) return response def endpoint_action_by_aro( self, action_type: EndpointActionType, aro_id: str, to_dataclass: bool = False ) -> dict | ActionByHostResponse: response: dict = self._request( method="POST", path=f"/endpoint/aro/{action_type.value}", json={"aro_id": aro_id}, ).json() if to_dataclass is True: return ActionByHostResponse(**response) return response def cloud_action_by_aro( self, action_type: CloudActionType, aro_id: str, to_dataclass: bool = False ) -> dict | ActionByCloudAccountResponse: response: dict = self._request( method="POST", path=f"/cloud/aro/{action_type.value}", json={"aro_id": aro_id}, ).json() if to_dataclass is True: return ActionByCloudAccountResponse(**response) return response # pylint: disable=too-many-arguments def _request( self, method: Literal["GET", "POST"], path: str, json: dict | None = None, data: dict | None = None, check_authentication: bool = True, ) -> Response: if check_authentication is True and not isinstance(self.session.headers.get("Authorization"), str): raise Exception("Must provide API Authorization to use Broker commands.") host_and_path = f"{self.host}{path}" if method == "GET": resp: Response = self.session.get(host_and_path, verify=self.verify_ssl, timeout=self.timeout) else: resp: Response = self.session.post( # type: ignore[no-redef] host_and_path, json=json, data=data, verify=self.verify_ssl, timeout=self.timeout ) resp.raise_for_status() return resp """ Commands """ def portal_check(): """ Poking to the portal to make sure it's up """ try: Portal(bearer=API_KEY) return True except Exception: demisto.debug(traceback.format_exc()) return False def fetch_incidents(last_run, first_run_time_range): last_fetch = last_run.get("last_fetch", None) aro_time_max = datetime.utcnow() - timedelta(seconds=1) if last_fetch is None: aro_time_min = aro_time_max - timedelta(days=first_run_time_range) else: aro_time_min = dateparser.parse(last_fetch) # type: ignore assert aro_time_min is not None p = Portal(bearer=API_KEY) query = {"resolution": "Unresolved", "since": aro_time_min.strftime(DATE_FORMAT), "until": aro_time_max.strftime(DATE_FORMAT)} aros = p.get_aros(query=query) incidents = [] # AROs are ordered by most recent ARO # it's required to traverse aros in chronological order (so last element first) # to avoid duplicating incidents for a in reversed(aros): created_time = dateparser.parse(a["creation_time"]) assert created_time is not None, f'could not parse {a["creation_time"]}' if created_time != last_fetch: created_time_str = created_time.strftime(DATE_FORMAT) if a.get("organization", None): org_name = a["organization"].get("name", "No org name") org_id = a["organization"].get("ID", None) else: org_name = "No org name" org_id = None aro_type = a.get("type", "No ARO type") aro_title = a.get("title", "No title") incident: Dict[str, Any] = { "name": f"""[{org_name}] [{aro_type}] {aro_title}""", "occured": created_time_str, "rawJSON": json.dumps(a), } if a.get("severity", None): # XSOAR mapping # Unknown: 0 # Informational: 0.5 # Low: 1 # Medium: 2 # High: 3 # Critical: 4 severity_from_portal = a["severity"] if severity_from_portal == "Informational": incident["severity"] = 0.5 elif severity_from_portal == "Warning": incident["severity"] = 1 elif severity_from_portal == "Low": incident["severity"] = 1 elif severity_from_portal == "Medium": incident["severity"] = 2 elif severity_from_portal == "High": incident["severity"] = 3 elif severity_from_portal == "Critical": incident["severity"] = 4 else: incident["severity"] = 0 if a.get("details", None): incident["details"] = a["details"] if a.get("steps", None) and len(a["steps"]) > 0: incident["details"] += "\n\nMitigation Steps\n" for step in a["steps"]: incident["details"] += f"""- {step['label']}\n""" if org_id: active_response_profile = p.get_active_response_profile(org_id) if active_response_profile: policy = active_response_profile.get("response_policy") options = active_response_profile.get("options") incident["details"] += "\nActive Response Profile\n" incident["details"] += f"""- Response policy: {policy}\n""" incident["details"] += f"""- Exclusions/ Modifications: {options}\n""" incidents.append(incident) next_run = {"last_fetch": aro_time_max.strftime(DATE_FORMAT)} return next_run, incidents def get_aros(): p = Portal(bearer=API_KEY) q = demisto.args().get("query", None) if q: query = {} # pragma: no cover for param in q.split("&"): key = param.split("=")[0] value = param.split("=")[1] query[key] = value if "org" in query: org = p.find_organizations(query["org"]) if not org: raise ValueError(f'Unknown organization named {query["org"]}') del query["org"] query["organization_id"] = org[0]["ID"] aros = p.get_aros(query=query) else: aros = p.get_aros() details = argToBoolean(demisto.args().get("details", "false")) keys = ["title", "organization", "resolution", "severity", "status", "type"] if not details: filtered_r = [] # returning only data in keys for aro in aros: a = {k: aro[k] for k in keys} filtered_r.append(a) return filtered_r else: return aros def list_organizations(): p = Portal(bearer=API_KEY) return p.get_organizations() def transition_aro_command(): p = Portal(bearer=API_KEY) args = demisto.args() return p.transition_aro(**args) def comment_aro_command(): p = Portal(bearer=API_KEY) args = demisto.args() return p.comment_aro(**args) def list_escalation_contacts_command(portal_instance, args): contacts = [] headers = ["priority", "first_name", "last_name", "job_title", "phone_number", "secondary_phone", "email", "notes"] result = portal_instance.list_escalation_contacts(**args) if result and result[0]["organization_id"] == args["org_id"]: for contact in result[0]["escalation_contacts"]: # Extract the required fields contact_info = { "priority": contact.get("priority"), "first_name": contact.get("first_name"), "last_name": contact.get("last_name"), "job_title": contact.get("job_title"), "phone_number": contact.get("phone_number"), "secondary_phone": contact.get("secondary_phone"), "email": contact.get("email"), "notes": contact.get("notes"), } contacts.append(contact_info) readable_output = tableToMarkdown( "Escalation Contacts", contacts, headers=headers, headerTransform=string_to_table_header ) else: readable_output = "No escalation contacts found." return CommandResults( readable_output=readable_output, outputs_prefix="FESPortal.Org", outputs_key_field="ID", outputs=contacts ) def list_organization_key_contacts_command(portal_instance, args): contacts = [] headers = ["first_name", "last_name", "phone_number", "email", "type"] result = portal_instance.list_organization_contacts(**args) if result: for contact in result: # Extract the required fields if contact["type"] in ["Administrative", "Technical - Primary", "Technical - Secondary", "Primary", "Technical"]: details_key = contact.get("contact_info") or contact.get("user") # Data can be in either key. contact_info = { "first_name": details_key.get("first_name"), "last_name": details_key.get("last_name"), "phone_number": details_key.get("phone_number"), "email": details_key.get("email"), "type": contact["type"], } contacts.append(contact_info) readable_output = tableToMarkdown("Key Contacts", contacts, headers=headers, headerTransform=string_to_table_header) else: readable_output = "No key contacts found." return CommandResults( readable_output=readable_output, outputs_prefix="FESPortal.Org", outputs_key_field="ID", outputs=contacts ) def list_organization_language_command(portal_instance, args): result = portal_instance.list_organization_language(**args) if result: readable_output = tableToMarkdown("Default Organization Language", result, headerTransform=string_to_table_header) else: readable_output = "No default language found." return CommandResults(readable_output=readable_output, outputs_prefix="FESPortal.Org", outputs_key_field="ID", outputs=result) """ Broker Commands """ def ping_broker_command(broker_instance: BrokerClient): result = broker_instance.ping() if "pong" in result: readable_output = "## Success" else: readable_output = f"Failure - {result}." return CommandResults( readable_output=readable_output, outputs_prefix="FESBroker.APIStatus", outputs_key_field="", outputs=result ) def list_organizations_broker_command(broker_instance: BrokerClient): result = broker_instance.organizations() if result: readable_output = tableToMarkdown("Organizations", result) else: readable_output = "No broker organizations found." return CommandResults(readable_output=readable_output, outputs_prefix="FESBroker.Org", outputs_key_field="ID", outputs=result) def endpoint_action_by_host_broker_command(broker_instance: BrokerClient, args): action_type = EndpointActionType[args["action_type"]] result = broker_instance.endpoint_action_by_host(action_type, args["org_id"], args["host_identifier"]) return CommandResults( readable_output=tableToMarkdown("Command Result - Success", result), outputs_prefix="FESBroker.Action", outputs_key_field="agent_uuid", outputs=result, ) def endpoint_action_by_aro_broker_command(broker_instance: BrokerClient, args): action_type = EndpointActionType[args["action_type"]] result = broker_instance.endpoint_action_by_aro(action_type, args["aro_id"]) return CommandResults( readable_output=tableToMarkdown("Command Result - Success", result), outputs_prefix="FESBroker.Action", outputs_key_field="agent_uuid", outputs=result, ) def cloud_action_by_aro_broker_command(broker_instance: BrokerClient, args): action_type = CloudActionType[args["action_type"]] result = broker_instance.cloud_action_by_aro(action_type, args["aro_id"]) return CommandResults( readable_output=tableToMarkdown("Command Result", result), outputs_prefix="FESBroker.Action", outputs_key_field="action_id", outputs=result, ) def main(): params = demisto.params() args = demisto.args() command = demisto.command() broker_url = params.get("broker_url", "") return_error_msg = None demisto.info(f"{command} is called") if broker_url and (command.startswith("cov-mgsec-broker") or command == "test-module"): # Initialize Broker client only if required, allowing the Portal commands to still function if the Broker # connection is down or unwanted. broker_instance = BrokerClient(host=broker_url, api_key=API_KEY) else: demisto.debug("No condition was met. Initializing BrokerClient") broker_instance = BrokerClient(host=broker_url, api_key=API_KEY) portal_instance = Portal(bearer=API_KEY) try: if command == "test-module": portal_result = portal_check() if broker_url: broker_result = bool(broker_instance.ping() == "pong") if broker_result is True and portal_result is True: return_results("ok") elif portal_result is True: return_results("ok") elif command == "fetch-incidents": next_run, incidents = fetch_incidents(last_run=demisto.getLastRun(), first_run_time_range=FIRST_RUN_TIME_RANGE) demisto.setLastRun(next_run) demisto.incidents(incidents) elif command == "cov-mgsec-get-aro": r = get_aros() if r: readable_output = tableToMarkdown("AROs", r, removeNull=True, headerTransform=string_to_table_header) else: readable_output = "No AROs found" results = CommandResults( outputs_prefix="FESPortal.ARO", outputs_key_field="ID", outputs=r, readable_output=readable_output ) return_results(results) elif command == "cov-mgsec-list-org": r = list_organizations() if r: readable_output = tableToMarkdown("Organizations", r, removeNull=True, headerTransform=string_to_table_header) else: readable_output = "No organizations found" results = CommandResults( outputs_prefix="FESPortal.Org", outputs_key_field="ID", outputs=r, readable_output=readable_output ) return_results(results) elif command == "cov-mgsec-transition-aro": r = transition_aro_command() if r: readable_output = tableToMarkdown("ARO", r, removeNull=True, headerTransform=string_to_table_header) else: readable_output = "Error transitioning ARO." results = CommandResults( outputs_prefix="FESPortal.Org", outputs_key_field="ID", outputs=r, readable_output=readable_output ) return_results(results) elif command == "cov-mgsec-comment-aro": r = comment_aro_command() if r: readable_output = tableToMarkdown("ARO", r, removeNull=True, headerTransform=string_to_table_header) else: readable_output = "Error commenting on ARO." results = CommandResults( outputs_prefix="FESPortal.Org", outputs_key_field="ID", outputs=r, readable_output=readable_output ) return_results(results) elif command == "cov-mgsec-list-escalation-contacts": return_results(list_escalation_contacts_command(portal_instance, args)) elif command == "cov-mgsec-list-key-contacts": return_results(list_organization_key_contacts_command(portal_instance, args)) elif command == "cov-mgsec-list-language": return_results(list_organization_language_command(portal_instance, args)) elif command == "cov-mgsec-broker-ping": return_results(ping_broker_command(broker_instance)) elif command == "cov-mgsec-broker-list-org": return_results(list_organizations_broker_command(broker_instance)) elif command == "cov-mgsec-broker-endpoint-action-by-host": return_results(endpoint_action_by_host_broker_command(broker_instance, args)) elif command == "cov-mgsec-broker-endpoint-action-by-aro": return_results(endpoint_action_by_aro_broker_command(broker_instance, args)) elif command == "cov-mgsec-broker-cloud-action-by-aro": return_results(cloud_action_by_aro_broker_command(broker_instance, args)) except HTTPError as e: demisto.error(traceback.format_exc()) http_text = None try: http_text = e.response.text # Try to extract a text response if it exists. except AttributeError: http_text = e.response return_error_msg = ( f"Failed to execute {command} command with HTTP response: {http_text!s}.\nStack trace: {traceback.format_exc()}" ) except Exception as e: demisto.error(traceback.format_exc()) return_error_msg = f"Failed to execute {command} command. Error: {e!s}.\nStack trace: {traceback.format_exc()}" if return_error_msg: return_error(return_error_msg) if __name__ in ("__main__", "__builtin__", "builtins"): main()