TrustwaveFusion

The Trustwave Fusion platform connects your organization’s digital footprint to a robust security cloud comprised of the Trustwave data lake, advanced analytics, actionable threat intelligence and a wide range of Trustwave services including Trustwave SpiderLabs , elite team of security specialists. Your team will benefit from deep visibility and the advanced security expertise necessary for protecting assets and eradicating threats as they arise.

Data Enrichment & Threat Intelligence · Trustwave Fusion

Details

IDTrustwaveFusion
ProviderLevelBlue
CategoryData Enrichment & Threat Intelligence
From Version5.0.0
Docker Imagedemisto/python3:3.12.13.10116658
Supported ModulesAgentix XSIAM

README

The Trustwave Fusion platform connects your organization’s digital footprint
to a robust security cloud comprised of the Trustwave data lake, advanced
analytics, actionable threat intelligence and a wide range of Trustwave
services including Trustwave SpiderLabs , elite team of security
specialists. Your team will benefit from deep visibility and the advanced
security expertise necessary for protecting assets and eradicating threats as
they arise.

This integration was integrated and tested with version 1.0.68 of TrustwaveFusion

Configure TrustwaveFusion in Cortex

Parameter Description Required
Fusion API URL   True
API Key The API Key to use for connection True
Trust any certificate (not secure)   False
Use system proxy settings   False
Fetch incidents   False
Incident type   False
Maximum number of incidents per fetch   False
First fetch time Format: [number] [time unit]. e.g., 12 hours, 7 days, 2 seconds etc. False
Ticket Types Types of tickets to fetch 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.

trustwave-get-ticket


Get a single ticket

Base Command

trustwave-get-ticket

Input

Argument Name Description Required
id Ticket ID. Required

Context Output

Path Type Description
Trustwave.Ticket.number String The Ticket ID
Trustwave.Ticket.subject String The ticket title.
Trustwave.Ticket.status String The status of the ticket
Trustwave.Ticket.description String The detailed ticket description.
Trustwave.Ticket.category String Ticket category
Trustwave.Ticket.createdBy String User that created the ticket.
Trustwave.Ticket.createdOn Date The ticket creation time.
Trustwave.Ticket.customerName String The name of the customer.
Trustwave.Ticket.findings.classification String Category for finding related to the ticket.
Trustwave.Ticket.findings.classificationCode String Category code for finding related to the ticket.
Trustwave.Ticket.findings.findingId String Id finding related to the ticket.
Trustwave.Ticket.formatted_notes String Human readable Notes for the ticket.
Trustwave.Ticket.impact String Ticket impact (HIGH, MEDIUM, LOW)
Trustwave.Ticket.notes.actor String User that added the note (comment)
Trustwave.Ticket.notes.text String The note (comment) text.
Trustwave.Ticket.notes.timestamp Date Time when the note (comment) was created.
Trustwave.Ticket.priority String Ticket priority (CRITICAL, HIGH, MEDIUM, LOW)
Trustwave.Ticket.subCategory String Ticket sub-category.
Trustwave.Ticket.type String The ticket type (CASE, INCIDENT, CHANGE)
Trustwave.Ticket.updatedOn Date When the ticket was last updated.
Trustwave.Ticket.urgency String The ticket urgency (HIGH, MEDIUM, LOW)

Command example

!trustwave-get-ticket id="INA1976568"

Context Example

{
    "Trustwave": {
        "Ticket": {
            "assetIds": [],
            "category": "Threat Detection & Response",
            "createdBy": "dummyuser",
            "createdOn": "2021-12-08T17:16:27.000+00:00",
            "customerName": "Sample Customer",
            "description": "Ticket description.",
            "findings": [
                {
                    "classification": null,
                    "classificationCode": "UnauthorizedAccessOrIntrusionAttempt.",
                    "findingId": "765432:THREAT:@AXv0k6GhG2zTcaogE1vG"
                }
            ],
            "formatted_notes": "2021-12-08T17:16:27.000+00:00 Created by: dummyuser\nNOTE:\nNote A\n----------------\n2021-12-08T17:17:57.000+00:00 Created by: dummyuser\nNOTE:\nNote B\n----------------\n2021-12-09T16:43:31.000+00:00 Created by: dummy_user\nNOTE:\nNote C",
            "impact": "HIGH",
            "notes": [
                {
                    "actor": "dummyuser",
                    "text": "Note A",
                    "timestamp": "2021-12-08T17:16:27.000+00:00"
                },
                {
                    "actor": "dummyuser",
                    "text": "Note B",
                    "timestamp": "2021-12-08T17:17:57.000+00:00"
                },
                {
                    "actor": "dummy_user",
                    "text": "Note C",
                    "timestamp": "2021-12-09T16:43:31.000+00:00"
                }
            ],
            "number": "INA1976568",
            "priority": "HIGH",
            "status": "ON_HOLD",
            "subCategory": "Threat Operations",
            "subject": "Test incident #354",
            "type": "INCIDENT",
            "updatedOn": "2021-12-09T16:43:48.000+00:00",
            "urgency": "MEDIUM"
        }
    }
}

Human Readable Output

field value
assetIds  
category Threat Detection & Response
createdBy dummyuser
createdOn 2021-12-08T17:16:27.000+00:00
customerName Sample Customer
description Ticket description.
findings {‘classification’: None, ‘classificationCode’: ‘UnauthorizedAccessOrIntrusionAttempt.’, ‘findingId’: ‘765432:THREAT:@AXv0k6GhG2zTcaogE1vG’}
impact HIGH
notes {‘actor’: ‘dummyuser’, ‘text’: ‘Note A’, ‘timestamp’: ‘2021-12-08T17:16:27.000+00:00’},
{‘actor’: ‘dummyuser’, ‘text’: ‘Note B’, ‘timestamp’: ‘2021-12-08T17:17:57.000+00:00’},
{‘actor’: ‘dummy_u….[Truncated]
number INA1976568
priority HIGH
status ON_HOLD
subCategory Threat Operations
subject Test incident #354
type INCIDENT
updatedOn 2021-12-09T16:43:48.000+00:00
urgency MEDIUM
formatted_notes 2021-12-08T17:16:27.000+00:00 Created by: dummyuser
NOTE:
Note A
—————-
2021-12-08T17:17:57.000+00:00 Created by: dummyuser
NOTE:
Note B
—————-
2021-12-09T1….[Truncated]

trustwave-search-tickets


Search tickets

Base Command

trustwave-search-tickets

Input

Argument Name Description Required
id Ticket ID. Optional
subject Ticket Subject. Optional
type Ticket type to query. Possible values are: INCIDENT, CASE, CHANGE. Optional
status Ticket status. Possible values are: NEW, OPEN, IN_PROGRESS, AWAITING_INFO, ON_HOLD, RESOLVED, CLOSED, CANCELED, SCHEDULED. Optional
priority Ticket priority. Possible values are: CRITICAL, HIGH, MEDIUM, LOW. Optional
impact Ticket impact. Possible values are: HIGH, MEDIUM, LOW. Optional
urgency Ticket urgency. Possible values are: HIGH, MEDIUM, LOW. Optional

Context Output

Path Type Description
Trustwave.Ticket.number String The Ticket ID
Trustwave.Ticket.subject String The ticket title.
Trustwave.Ticket.status String The status of the ticket
Trustwave.Ticket.description String The detailed ticket description.
Trustwave.Ticket.category String Ticket category.
Trustwave.Ticket.createdBy String User that created the ticket.
Trustwave.Ticket.createdOn Date The time when the ticket was created.
Trustwave.Ticket.customerName String The name of the customer.
Trustwave.Ticket.formatted_notes String Human readable notes.
Trustwave.Ticket.impact String Ticket impact (HIGH, MEDIUM, LOW)
Trustwave.Ticket.notes.actor String User that added the note (comment)
Trustwave.Ticket.notes.text String The note (comment) text.
Trustwave.Ticket.notes.timestamp Date Time when the note (comment) was created.
Trustwave.Ticket.priority String Ticket priority (CRITICAL, HIGH, MEDIUM, LOW)
Trustwave.Ticket.subCategory String Ticket sub-category.
Trustwave.Ticket.type String The ticket type (CASE, INCIDENT, CHANGE)
Trustwave.Ticket.updatedOn Date When the ticket was last updated.
Trustwave.Ticket.urgency String The ticket urgency (HIGH, MEDIUM, LOW)

Command example

!trustwave-search-tickets limit=2 type=INCIDENT

Context Example

{
    "Trustwave": {
        "Ticket": [
            {
                "assetIds": [
                    "765432:managed-device#DEVICE:AW8Qp1Bextjwd2cF57Mk"
                ],
                "category": "Technology Management",
                "createdBy": "cpe_outage_service",
                "createdOn": "2021-11-29T10:56:45.000+00:00",
                "customerName": "Sample Customer",
                "description": "",
                "findings": [],
                "formatted_notes": "2021-11-29T10:56:44.000+00:00 Created by: cpe_outage_service\nNOTE:\nNOTE A",
                "impact": "HIGH",
                "notes": [
                    {
                        "actor": "cpe_outage_service",
                        "text": "NOTE A",
                        "timestamp": "2021-11-29T10:56:44.000+00:00"
                    }
                ],
                "number": "INA1077007",
                "priority": "HIGH",
                "status": "CLOSED",
                "subCategory": "Cellular Backup",
                "subject": "Alert: device is using cellular",
                "type": "INCIDENT",
                "updatedOn": "2021-12-29T00:00:08.000+00:00",
                "urgency": "MEDIUM"
            },
            {
                "assetIds": [],
                "category": "Threat Detection & Response",
                "createdBy": "dummyuser",
                "createdOn": "2021-12-08T17:16:27.000+00:00",
                "customerName": "Sample Customer",
                "description": "Ticket description",
                "findings": [
                    {
                        "classification": null,
                        "classificationCode": "UnauthorizedAccessOrIntrusionAttempt.",
                        "findingId": "765432:THREAT:@AXv0k6GhG2zTcaogE1vG"
                    }
                ],
                "formatted_notes": "2021-12-08T17:16:27.000+00:00 Created by: dummyuser\nNOTE:\nSample Note.\n----------------\n2021-12-08T17:17:57.000+00:00 Created by: dummyuser\nNOTE:\nSample note #2.\n----------------\n2021-12-09T16:43:31.000+00:00 Created by: dummy_user\nNOTE:\nSample note #3",
                "impact": "HIGH",
                "notes": [
                    {
                        "actor": "dummyuser",
                        "text": "Sample Note.",
                        "timestamp": "2021-12-08T17:16:27.000+00:00"
                    },
                    {
                        "actor": "dummyuser",
                        "text": "Sample note #2.",
                        "timestamp": "2021-12-08T17:17:57.000+00:00"
                    },
                    {
                        "actor": "dummy_user",
                        "text": "Sample note #3",
                        "timestamp": "2021-12-09T16:43:31.000+00:00"
                    }
                ],
                "number": "INA1077535",
                "priority": "HIGH",
                "status": "ON_HOLD",
                "subCategory": "Threat Operations",
                "subject": "MCAS - Impossible travel activity",
                "type": "INCIDENT",
                "updatedOn": "2021-12-09T16:43:48.000+00:00",
                "urgency": "MEDIUM"
            }
        ]
    }
}

Human Readable Output

Results

assetIds category createdBy createdOn customerName description findings formatted_notes impact notes number priority status subCategory subject type updatedOn urgency
765432:managed-device#DEVICE:AW8Qp1Bextjwd2cF57Mk Technology Management cpe_outage_service 2021-11-29T10:56:45.000+00:00 Sample Customer     2021-11-29T10:56:44.000+00:00 Created by: cpe_outage_service
NOTE:
NOTE A
HIGH {‘actor’: ‘cpe_outage_service’, ‘text’: ‘NOTE A’, ‘timestamp’: ‘2021-11-29T10:56:44.000+00:00’} INA1077007 HIGH CLOSED Cellular Backup Alert: device is using cellular INCIDENT 2021-12-29T00:00:08.000+00:00 MEDIUM
  Threat Detection & Response dummyuser 2021-12-08T17:16:27.000+00:00 Sample Customer Ticket description {‘classification’: None, ‘classificationCode’: ‘UnauthorizedAccessOrIntrusionAttempt.’, ‘findingId’: ‘765432:THREAT:@AXv0k6GhG2zTcaogE1vG’} 2021-12-08T17:16:27.000+00:00 Created by: dummyuser
NOTE:
Sample Note.
—————-
2021-12-08T17:17:57.000+00:00 Created by: dummyuser
NOTE:
Sample note #2.
—————-
2021-12-09T16:43:31.000+00:00 Created by: dummy_user
NOTE:
Sample note #3
HIGH {‘actor’: ‘dummyuser’, ‘text’: ‘Sample Note.’, ‘timestamp’: ‘2021-12-08T17:16:27.000+00:00’},
{‘actor’: ‘dummyuser’, ‘text’: ‘Sample note #2.’, ‘timestamp’: ‘2021-12-08T17:17:57.000+00:00’},
{‘actor’: ‘dummy_user’, ‘text’: ‘Sample note #3’, ‘timestamp’: ‘2021-12-09T16:43:31.000+00:00’}
INA1077535 HIGH ON_HOLD Threat Operations MCAS - Impossible travel activity INCIDENT 2021-12-09T16:43:48.000+00:00 MEDIUM

trustwave-add-ticket-comment


Add a comment to a ticket

Base Command

trustwave-add-ticket-comment

Input

Argument Name Description Required
id Incident ID. Required
comment Comment text. Required

Context Output

There is no context output for this command.

Command example

!trustwave-add-ticket-comment id=INA1051028 comment="test from xsoar"

Human Readable Output

Success

trustwave-close-ticket


Close a ticket

Base Command

trustwave-close-ticket

Input

Argument Name Description Required
id Incident ID. Required
comment Comment text. Required

Context Output

There is no context output for this command.

Command example

!trustwave-close-ticket id="INA1051028" comment="Comment"

Human Readable Output

Success

trustwave-get-updated-tickets


Get updated tickets.

Base Command

trustwave-get-updated-tickets

Input

Argument Name Description Required
since Absolute or relative date to check for updates. Required
fetch_limit Maximum number of tickets to fetch. Default is 100. Optional
ticket_types Ticket type to query. Possible values are: INCIDENT, CASE, CHANGE. Default is INCIDENT. Optional

Context Output

Path Type Description
Trustwave.Ticket.number String The Ticket ID
Trustwave.Ticket.subject String The ticket title.
Trustwave.Ticket.status String The status of the ticket
Trustwave.Ticket.description String The detailed ticket description.
Trustwave.Ticket.category String Ticket category.
Trustwave.Ticket.createdBy String User that created the ticket.
Trustwave.Ticket.createdOn Date The time when the ticket was created.
Trustwave.Ticket.customerName String The name of the customer.
Trustwave.Ticket.formatted_notes String Human readable notes.
Trustwave.Ticket.impact String Ticket impact (HIGH, MEDIUM, LOW)
Trustwave.Ticket.notes.actor String User that added the note (comment)
Trustwave.Ticket.notes.text String The note (comment) text.
Trustwave.Ticket.notes.timestamp Date Time when the note (comment) was created.
Trustwave.Ticket.priority String Ticket priority (CRITICAL, HIGH, MEDIUM, LOW)
Trustwave.Ticket.subCategory String Ticket sub-category.
Trustwave.Ticket.type String The ticket type (CASE, INCIDENT, CHANGE)
Trustwave.Ticket.updatedOn Date When the ticket was last updated.
Trustwave.Ticket.urgency String The ticket urgency (HIGH, MEDIUM, LOW)

Command example

!trustwave-get-updated-tickets fetch_limit=2 since=2021-12-09T16:43:48.000+00:00

Context Example

{
    "Trustwave": {
        "Ticket": [
            {
                "assetIds": [
                    "765432:managed-device#DEVICE:AW8Qp1Bextjwd2cF57Mk"
                ],
                "category": "Technology Management",
                "createdBy": "cpe_outage_service",
                "createdOn": "2021-11-29T10:56:45.000+00:00",
                "customerName": "Sample Customer",
                "description": "",
                "findings": [],
                "formatted_notes": "2021-11-29T10:56:44.000+00:00 Created by: cpe_outage_service\nNOTE:\nNOTE A",
                "impact": "HIGH",
                "notes": [
                    {
                        "actor": "cpe_outage_service",
                        "text": "NOTE A",
                        "timestamp": "2021-11-29T10:56:44.000+00:00"
                    }
                ],
                "number": "INA1077007",
                "priority": "HIGH",
                "status": "CLOSED",
                "subCategory": "Cellular Backup",
                "subject": "Alert: device is using cellular",
                "type": "INCIDENT",
                "updatedOn": "2021-12-29T00:00:08.000+00:00",
                "urgency": "MEDIUM"
            },
            {
                "assetIds": [],
                "category": "Threat Detection & Response",
                "createdBy": "dummyuser",
                "createdOn": "2021-12-08T17:16:27.000+00:00",
                "customerName": "Sample Customer",
                "description": "Ticket description",
                "findings": [
                    {
                        "classification": null,
                        "classificationCode": "UnauthorizedAccessOrIntrusionAttempt.",
                        "findingId": "765432:THREAT:@AXv0k6GhG2zTcaogE1vG"
                    }
                ],
                "formatted_notes": "2021-12-08T17:16:27.000+00:00 Created by: dummyuser\nNOTE:\nSample Note.\n----------------\n2021-12-08T17:17:57.000+00:00 Created by: dummyuser\nNOTE:\nSample note #2.\n----------------\n2021-12-09T16:43:31.000+00:00 Created by: dummy_user\nNOTE:\nSample note #3",
                "impact": "HIGH",
                "notes": [
                    {
                        "actor": "dummyuser",
                        "text": "Sample Note.",
                        "timestamp": "2021-12-08T17:16:27.000+00:00"
                    },
                    {
                        "actor": "dummyuser",
                        "text": "Sample note #2.",
                        "timestamp": "2021-12-08T17:17:57.000+00:00"
                    },
                    {
                        "actor": "dummy_user",
                        "text": "Sample note #3",
                        "timestamp": "2021-12-09T16:43:31.000+00:00"
                    }
                ],
                "number": "INA1077535",
                "priority": "HIGH",
                "status": "ON_HOLD",
                "subCategory": "Threat Operations",
                "subject": "MCAS - Impossible travel activity",
                "type": "INCIDENT",
                "updatedOn": "2021-12-09T16:43:48.000+00:00",
                "urgency": "MEDIUM"
            }
        ]
    }
}

Human Readable Output

Results

assetIds category createdBy createdOn customerName description findings formatted_notes impact notes number priority status subCategory subject type updatedOn urgency
765432:managed-device#DEVICE:AW8Qp1Bextjwd2cF57Mk Technology Management cpe_outage_service 2021-11-29T10:56:45.000+00:00 Sample Customer     2021-11-29T10:56:44.000+00:00 Created by: cpe_outage_service
NOTE:
NOTE A
HIGH {‘actor’: ‘cpe_outage_service’, ‘text’: ‘NOTE A’, ‘timestamp’: ‘2021-11-29T10:56:44.000+00:00’} INA1077007 HIGH CLOSED Cellular Backup Alert: device is using cellular INCIDENT 2021-12-29T00:00:08.000+00:00 MEDIUM
  Threat Detection & Response dummyuser 2021-12-08T17:16:27.000+00:00 Sample Customer Ticket description {‘classification’: None, ‘classificationCode’: ‘UnauthorizedAccessOrIntrusionAttempt.’, ‘findingId’: ‘765432:THREAT:@AXv0k6GhG2zTcaogE1vG’} 2021-12-08T17:16:27.000+00:00 Created by: dummyuser
NOTE:
Sample Note.
—————-
2021-12-08T17:17:57.000+00:00 Created by: dummyuser
NOTE:
Sample note #2.
—————-
2021-12-09T16:43:31.000+00:00 Created by: dummy_user
NOTE:
Sample note #3
HIGH {‘actor’: ‘dummyuser’, ‘text’: ‘Sample Note.’, ‘timestamp’: ‘2021-12-08T17:16:27.000+00:00’},
{‘actor’: ‘dummyuser’, ‘text’: ‘Sample note #2.’, ‘timestamp’: ‘2021-12-08T17:17:57.000+00:00’},
{‘actor’: ‘dummy_user’, ‘text’: ‘Sample note #3’, ‘timestamp’: ‘2021-12-09T16:43:31.000+00:00’}
INA1077535 HIGH ON_HOLD Threat Operations MCAS - Impossible travel activity INCIDENT 2021-12-09T16:43:48.000+00:00 MEDIUM

trustwave-search-findings


Search for Findings

Base Command

trustwave-search-findings

Input

Argument Name Description Required
id Finding ID. Optional
limit Maximum number of Findings to return. Default is 100. Optional
name Name of the Finding. Optional
classification Finding Classification. Optional
summary Finding summary. Optional
detail Finding detail. Optional
priority Finding priority. Possible values are: CRITICAL, HIGH, MEDIUM, LOW. Optional
severity Finding severity. Optional
created_since created_since. Optional
updated_since Updated since. Optional

Context Output

Path Type Description
Trustwave.Finding.classification String Category for the finding
Trustwave.Finding.createdOn Date Time when the finding was created.
Trustwave.Finding.customerName String Customer name for the finding.
Trustwave.Finding.destination String Destination for finding.
Trustwave.Finding.detail String Detailed description of the finding.
Trustwave.Finding.id String The finding ID
Trustwave.Finding.priority Number The priority of the finding.
Trustwave.Finding.severity Number The severity of the finding.
Trustwave.Finding.source String SOAR actions taken for finding.
Trustwave.Finding.status.description String Current status of the Finding.
Trustwave.Finding.summary String The name of the finding.
Trustwave.Finding.type String The type of finding (e.g. THREAT, VULNERABILITY)
Trustwave.Finding.updatedOn Date Time when the finding was last updated.

Command example

!trustwave-search-findings limit="2" summary="MS Graph Alert Detection Rule" type=threat updatedSince=2021-12-08T17:17:56.000+00:00

Context Example

{
    "Trustwave": {
        "Finding": [
            {
                "classification": null,
                "createdOn": "2021-09-17T16:26:11.731+00:00",
                "customerName": "Sample Customer",
                "destination": null,
                "detail": "MCAS - Impossible travel activity",
                "id": "765432:THREAT:@AXv0k6GhG2zTcaogE1vG",
                "priority": 4,
                "severity": 4,
                "source": null,
                "status": {
                    "description": "Security Incident"
                },
                "summary": "MS Graph Alert Detection Rule",
                "type": "THREAT",
                "updatedOn": "2021-12-08T17:17:56.504+00:00"
            },
            {
                "classification": null,
                "createdOn": "2021-10-26T22:33:17.567+00:00",
                "customerName": "Sample Customer",
                "destination": null,
                "detail": "ASC, MSTIC - Windows registry persistence method detected pqa VM_RegistryPersistencyKey",
                "id": "765432:THREAT:@AXy-u5fVt3G3ZYM6G5cH",
                "priority": 2,
                "severity": 2,
                "source": null,
                "status": {
                    "description": "False Positive"
                },
                "summary": "MS Graph Alert Detection Rule",
                "type": "THREAT",
                "updatedOn": "2021-12-16T17:21:31.384+00:00"
            }
        ]
    }
}

Human Readable Output

Results

classification createdOn customerName destination detail id priority severity source status summary type updatedOn
  2021-09-17T16:26:11.731+00:00 Sample Customer   MCAS - Impossible travel activity 765432:THREAT:@AXv0k6GhG2zTcaogE1vG 4 4   description: Security Incident MS Graph Alert Detection Rule THREAT 2021-12-08T17:17:56.504+00:00
  2021-10-26T22:33:17.567+00:00 Sample Customer   ASC, MSTIC - Windows registry persistence method detected pqa VM_RegistryPersistencyKey 765432:THREAT:@AXy-u5fVt3G3ZYM6G5cH 2 2   description: False Positive MS Graph Alert Detection Rule THREAT 2021-12-16T17:21:31.384+00:00

trustwave-get-finding


Get a Finding

Base Command

trustwave-get-finding

Input

Argument Name Description Required
id Finding ID. Required

Context Output

Path Type Description
Trustwave.Finding.analystNotes.actor String User that created the analyst note.
Trustwave.Finding.analystNotes.text String Analyst note text.
Trustwave.Finding.analystNotes.timestamp Date Time when the note was created
Trustwave.Finding.assetsIds String Assets impacted by the finding
Trustwave.Finding.classification String Category for the finding
Trustwave.Finding.createdOn Date Time when the finding was created.
Trustwave.Finding.customerName String Customer name for the finding.
Trustwave.Finding.destination String Destination for finding.
Trustwave.Finding.detail String Detailed description of the finding.
Trustwave.Finding.eventsIds String List of event ID associated with the finding.
Trustwave.Finding.id String The finding ID
Trustwave.Finding.parentId String The ID of the parent of the finding.
Trustwave.Finding.priority Number The priority of the finding.
Trustwave.Finding.severity Number The severity of the finding.
Trustwave.Finding.source String Source for finding
Trustwave.Finding.status.description String Current status of the Finding.
Trustwave.Finding.summary String The name of the finding.
Trustwave.Finding.type String The type of finding (e.g. THREAT, VULNERABILITY)
Trustwave.Finding.updatedOn Date Time when the finding was last updated.

Command example

!trustwave-get-finding id="765432:THREAT:@AXv0k6GhG2zTcaogE1vG"

Context Example

{
    "Trustwave": {
        "Finding": {
            "analystNotes": [
                {
                    "actor": "dummyuser",
                    "text": "Note A",
                    "timestamp": "2021-12-08T17:17:56.790+00:00"
                }
            ],
            "assetsIds": [
                "765432:PERSON:AXsNy0R8CfYgZQumlNdv"
            ],
            "childFindingIds": [],
            "classification": null,
            "createdOn": "2021-09-17T16:26:11.731+00:00",
            "customerName": "Sample Customer",
            "destination": null,
            "detail": "MCAS - Impossible travel activity",
            "eventsIds": [
                "34c0e1b2-96e6-4a25-be3d-80d0671a5d8f"
            ],
            "id": "765432:THREAT:@AXv0k6GhG2zTcaogE1vG",
            "parentId": null,
            "priority": 4,
            "severity": 4,
            "source": null,
            "status": {
                "description": "Security Incident"
            },
            "summary": "MS Graph Alert Detection Rule",
            "type": "THREAT",
            "updatedOn": "2021-12-08T17:17:56.504+00:00"
        }
    }
}

Human Readable Output

Results

analystNotes assetsIds childFindingIds classification createdOn customerName destination detail eventsIds id parentId priority severity source status summary type updatedOn
{‘actor’: ‘dummyuser’, ‘text’: ‘Note A’, ‘timestamp’: ‘2021-12-08T17:17:56.790+00:00’} 765432:PERSON:AXsNy0R8CfYgZQumlNdv     2021-09-17T16:26:11.731+00:00 Sample Customer   MCAS - Impossible travel activity 34c0e1b2-96e6-4a25-be3d-80d0671a5d8f 765432:THREAT:@AXv0k6GhG2zTcaogE1vG   4 4   description: Security Incident MS Graph Alert Detection Rule THREAT 2021-12-08T17:17:56.504+00:00

trustwave-get-asset


Get an Asset

Base Command

trustwave-get-asset

Input

Argument Name Description Required
id Asset ID. Required

Context Output

Path Type Description
Trustwave.Asset.cidr String Network address in CIDR notation.
Trustwave.Asset.createdOn Date Time when the asset was created.
Trustwave.Asset.customerName String The customer name for the asset.
Trustwave.Asset.id String The asset ID.
Trustwave.Asset.ips String List of IP addresses for the asset.
Trustwave.Asset.lastActivity Date Time of the last activity associated with the asset.
Trustwave.Asset.name String The name of the asset.
Trustwave.Asset.networkInterfaces.gateway String Gateway address for the asset network interface.
Trustwave.Asset.networkInterfaces.hostnames String List of hostname for the asset network interface
Trustwave.Asset.networkInterfaces.ip String IP address for the asset network interface
Trustwave.Asset.networkInterfaces.macAddress String MAC address for the asset network interface
Trustwave.Asset.networkInterfaces.macVendor String Vendor associated with the MAC address for the asset network interface
Trustwave.Asset.networkInterfaces.subnet String Network subnet the asset network interface
Trustwave.Asset.os String Asset operating system.
Trustwave.Asset.services.applicationName String Service name.
Trustwave.Asset.services.applicationProtocol String Service protocol.
Trustwave.Asset.services.port Number Service port
Trustwave.Asset.services.transportProtocol String Service transport.
Trustwave.Asset.status String Asset status.
Trustwave.Asset.type String The type of asset.
Trustwave.Asset.updatedOn Date Time when the asset was last updated.
Trustwave.Asset.uri String URI of the asset.

Command example

!trustwave-get-asset id="765432:DNA#DEVICE:AW2X-hCmXdgvNlcDpVGf"

Context Example

{
    "Trustwave": {
        "Asset": {
            "cidr": null,
            "createdOn": "2019-10-04T18:13:30.941+00:00",
            "customerName": "Sample Customer",
            "id": "765432:DNA#DEVICE:AW2X-hCmXdgvNlcDpVGf",
            "ips": [
                "10.103.201.47"
            ],
            "lastActivity": null,
            "name": "host.example.com",
            "networkInterfaces": [
                {
                    "gateway": null,
                    "hostnames": [
                        "host.example.com"
                    ],
                    "ip": "10.103.201.47",
                    "macAddress": null,
                    "macVendor": null,
                    "subnet": null
                }
            ],
            "notes": [],
            "os": null,
            "services": [
                {
                    "applicationName": null,
                    "applicationProtocol": null,
                    "port": 80,
                    "transportProtocol": "tcp"
                },
                {
                    "applicationName": null,
                    "applicationProtocol": "sunrpcportmap",
                    "port": 111,
                    "transportProtocol": "tcp"
                },
                {
                    "applicationName": null,
                    "applicationProtocol": "sunrpcportmap",
                    "port": 111,
                    "transportProtocol": "udp"
                },
                {
                    "applicationName": null,
                    "applicationProtocol": "ntp",
                    "port": 123,
                    "transportProtocol": "udp"
                },
                {
                    "applicationName": null,
                    "applicationProtocol": "snmp",
                    "port": 161,
                    "transportProtocol": "udp"
                },
                {
                    "applicationName": null,
                    "applicationProtocol": null,
                    "port": 443,
                    "transportProtocol": "tcp"
                },
                {
                    "applicationName": null,
                    "applicationProtocol": "mysql",
                    "port": 3306,
                    "transportProtocol": "tcp"
                },
                {
                    "applicationName": null,
                    "applicationProtocol": null,
                    "port": 5672,
                    "transportProtocol": "tcp"
                },
                {
                    "applicationName": null,
                    "applicationProtocol": "snmp",
                    "port": 16161,
                    "transportProtocol": "udp"
                }
            ],
            "status": null,
            "tags": [],
            "type": "Device",
            "updatedOn": "2019-10-04T19:09:59.907+00:00",
            "uri": null
        }
    }
}

Human Readable Output

Results

cidr createdOn customerName id ips lastActivity name networkInterfaces notes os services status tags type updatedOn uri
  2019-10-04T18:13:30.941+00:00 Sample Customer 765432:DNA#DEVICE:AW2X-hCmXdgvNlcDpVGf 10.103.201.47   host.example.com {‘gateway’: None, ‘hostnames’: [‘host.example.com’], ‘ip’: ‘10.103.201.47’, ‘macAddress’: None, ‘macVendor’: None, ‘subnet’: None}     {‘applicationName’: None, ‘applicationProtocol’: None, ‘port’: 80, ‘transportProtocol’: ‘tcp’},
{‘applicationName’: None, ‘applicationProtocol’: ‘sunrpcportmap’, ‘port’: 111, ‘transportProtocol’: ‘tcp’},
{‘applicationName’: None, ‘applicationProtocol’: ‘sunrpcportmap’, ‘port’: 111, ‘transportProtocol’: ‘udp’},
{‘applicationName’: None, ‘applicationProtocol’: ‘ntp’, ‘port’: 123, ‘transportProtocol’: ‘udp’},
{‘applicationName’: None, ‘applicationProtocol’: ‘snmp’, ‘port’: 161, ‘transportProtocol’: ‘udp’},
{‘applicationName’: None, ‘applicationProtocol’: None, ‘port’: 443, ‘transportProtocol’: ‘tcp’},
{‘applicationName’: None, ‘applicationProtocol’: ‘mysql’, ‘port’: 3306, ‘transportProtocol’: ‘tcp’},
{‘applicationName’: None, ‘applicationProtocol’: None, ‘port’: 5672, ‘transportProtocol’: ‘tcp’},
{‘applicationName’: None, ‘applicationProtocol’: ‘snmp’, ‘port’: 16161, ‘transportProtocol’: ‘udp’}
    Device 2019-10-04T19:09:59.907+00:00  

trustwave-search-assets


Search for Assets

Base Command

trustwave-search-assets

Input

Argument Name Description Required
id Asset ID. Optional
limit Maximum number of Assets to return. Default is 100. Optional
name Name of the Asset. Optional
os OS for the Asset. Optional
tags Asset Tags to search for. Optional
port port. Optional
app_protocol Application Protocol. Optional
transport Transport Protocol. Optional
type Asset Type. Optional
created_since created_since. Optional
updated_since Updated since. Optional

Context Output

Path Type Description
Trustwave.Asset.cidr String Network address in CIDR notation.
Trustwave.Asset.createdOn Date Time when the asset was created.
Trustwave.Asset.customerName String The customer name for the asset.
Trustwave.Asset.id String The asset ID.
Trustwave.Asset.ips String List of IP addresses for the asset.
Trustwave.Asset.lastActivity Date Time of the last activity associated with the asset.
Trustwave.Asset.name String The name of the asset.
Trustwave.Asset.networkInterfaces.gateway String Gateway address for the asset network interface.
Trustwave.Asset.networkInterfaces.hostnames String List of hostname for the asset network interface
Trustwave.Asset.networkInterfaces.ip String IP address for the asset network interface
Trustwave.Asset.networkInterfaces.macAddress String MAC address for the asset network interface
Trustwave.Asset.networkInterfaces.macVendor String Vendor associated with the MAC address for the asset network interface
Trustwave.Asset.networkInterfaces.subnet String Network subnet the asset network interface
Trustwave.Asset.os String Asset operating system.
Trustwave.Asset.status String Asset status.
Trustwave.Asset.type String The type of asset.
Trustwave.Asset.updatedOn Date Time when the asset was last updated.
Trustwave.Asset.uri String URI of the asset.

Command example

!trustwave-search-assets limit=2 type="DEVICE" name="host.example.com"

Context Example

{
    "Trustwave": {
        "Asset": {
            "cidr": null,
            "createdOn": "2019-10-04T18:13:30.941+00:00",
            "customerName": "Sample Customer",
            "id": "765432:DNA#DEVICE:AW2X-hCmXdgvNlcDpVGf",
            "ips": [
                "10.103.201.47"
            ],
            "lastActivity": null,
            "name": "host.example.com",
            "networkInterfaces": [
                {
                    "gateway": null,
                    "hostnames": [
                        "host.example.com"
                    ],
                    "ip": "10.103.201.47",
                    "macAddress": null,
                    "macVendor": null,
                    "subnet": null
                }
            ],
            "notes": [],
            "os": null,
            "services": [
                {
                    "applicationName": null,
                    "applicationProtocol": null,
                    "port": 80,
                    "transportProtocol": "tcp"
                },
                {
                    "applicationName": null,
                    "applicationProtocol": "sunrpcportmap",
                    "port": 111,
                    "transportProtocol": "tcp"
                },
                {
                    "applicationName": null,
                    "applicationProtocol": "sunrpcportmap",
                    "port": 111,
                    "transportProtocol": "udp"
                },
                {
                    "applicationName": null,
                    "applicationProtocol": "ntp",
                    "port": 123,
                    "transportProtocol": "udp"
                },
                {
                    "applicationName": null,
                    "applicationProtocol": "snmp",
                    "port": 161,
                    "transportProtocol": "udp"
                },
                {
                    "applicationName": null,
                    "applicationProtocol": null,
                    "port": 443,
                    "transportProtocol": "tcp"
                },
                {
                    "applicationName": null,
                    "applicationProtocol": "mysql",
                    "port": 3306,
                    "transportProtocol": "tcp"
                },
                {
                    "applicationName": null,
                    "applicationProtocol": null,
                    "port": 5672,
                    "transportProtocol": "tcp"
                },
                {
                    "applicationName": null,
                    "applicationProtocol": "snmp",
                    "port": 16161,
                    "transportProtocol": "udp"
                }
            ],
            "status": null,
            "tags": [],
            "type": "Device",
            "updatedOn": "2019-10-04T19:09:59.907+00:00",
            "uri": null
        }
    }
}

Human Readable Output

Results

cidr createdOn customerName id ips lastActivity name networkInterfaces notes os services status tags type updatedOn uri
  2019-10-04T18:13:30.941+00:00 Sample Customer 765432:DNA#DEVICE:AW2X-hCmXdgvNlcDpVGf 10.103.201.47   host.example.com {‘gateway’: None, ‘hostnames’: [‘host.example.com’], ‘ip’: ‘10.103.201.47’, ‘macAddress’: None, ‘macVendor’: None, ‘subnet’: None}     {‘applicationName’: None, ‘applicationProtocol’: None, ‘port’: 80, ‘transportProtocol’: ‘tcp’},
{‘applicationName’: None, ‘applicationProtocol’: ‘sunrpcportmap’, ‘port’: 111, ‘transportProtocol’: ‘tcp’},
{‘applicationName’: None, ‘applicationProtocol’: ‘sunrpcportmap’, ‘port’: 111, ‘transportProtocol’: ‘udp’},
{‘applicationName’: None, ‘applicationProtocol’: ‘ntp’, ‘port’: 123, ‘transportProtocol’: ‘udp’},
{‘applicationName’: None, ‘applicationProtocol’: ‘snmp’, ‘port’: 161, ‘transportProtocol’: ‘udp’},
{‘applicationName’: None, ‘applicationProtocol’: None, ‘port’: 443, ‘transportProtocol’: ‘tcp’},
{‘applicationName’: None, ‘applicationProtocol’: ‘mysql’, ‘port’: 3306, ‘transportProtocol’: ‘tcp’},
{‘applicationName’: None, ‘applicationProtocol’: None, ‘port’: 5672, ‘transportProtocol’: ‘tcp’},
{‘applicationName’: None, ‘applicationProtocol’: ‘snmp’, ‘port’: 16161, ‘transportProtocol’: ‘udp’}
    Device 2019-10-04T19:09:59.907+00:00  

Configuration parameters

  • url — Fusion API URL (required)
  • apikey — API Key (required)
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings
  • isFetch — Fetch incidents
  • incidentType — Incident type
  • incidentFetchInterval — Incidents Fetch Interval
  • max_fetch — Maximum number of incidents per fetch
  • first_fetch — First fetch time
  • ticket_types — Ticket Types

Commands (9)

  • trustwave-add-ticket-comment

    Add a comment to a ticket.

  • trustwave-close-ticket

    Close a ticket.

  • trustwave-get-asset

    Get an Asset.

  • trustwave-get-finding

    Get a Finding.

  • trustwave-get-ticket

    Get a single ticket.

  • trustwave-get-updated-tickets

    Get updated tickets.

  • trustwave-search-assets

    Search for Assets.

  • trustwave-search-findings

    Search for Findings.

  • trustwave-search-tickets

    Search tickets.

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

"""Trustwave Fusion Integration for Cortex XSOAR"""

from CommonServerUserPython import *  # noqa

import urllib3
import traceback
from typing import Any
import urllib.parse

# Disable insecure warnings
urllib3.disable_warnings()  # pylint: disable=no-member


""" CONSTANTS """

# Date format for Fusion searches
FUSION_DATE_FORMAT = "%Y-%m-%dT%H:%M:%SZ"

# Map Fusion priority to Demisto severity
SEVERITY_MAP = {
    "INFO": 0.5,
    "LOW": 1,
    "MEDIUM": 2,
    "HIGH": 3,
    "CRITICAL": 4,
}


TICKET_FIELDS = [
    "assetIds",
    "category",
    "createdBy",
    "createdOn",
    "customerName",
    "description",
    "findings",
    "impact",
    "notes",
    "number",
    "priority",
    "status",
    "subCategory",
    "subject",
    "type",
    "updatedOn",
    "urgency",
    "formatted_notes",
]

FINDING_FIELDS = [
    "analystNotes",
    "assetsIds",
    "childFindingIds",
    "classification",
    "createdOn",
    "customerName",
    "destination",
    "detail",
    "eventsIds",
    "id",
    "parentId",
    "priority",
    "severity",
    "source",
    "status",
    "summary",
    "type",
    "updatedOn",
]

ASSET_FIELDS = [
    "cidr",
    "createdOn",
    "customerName",
    "id",
    "ips",
    "lastActivity",
    "name",
    "networkInterfaces",
    "notes",
    "os",
    "services",
    "status",
    "tags",
    "type",
    "updatedOn",
    "uri",
]

""" CLIENT CLASS """


class Client(BaseClient):
    """
    Trustwave Fusion API Client
    """

    def search_tickets(self, **kwargs):
        result = self._http_request(method="GET", url_suffix="/v2/tickets", params=kwargs)
        tickets = []
        if result:
            tickets = result.get("items", [])

        # add formatted_notes field
        for tkt in tickets:
            if tkt["notes"]:
                tkt["formatted_notes"] = format_notes(tkt["notes"])
            else:
                tkt["formatted_notes"] = ""
            simplify_ticket(tkt)

        return tickets

    def describe(self):
        return self._http_request(method="GET", url_suffix="/v2/describe")

    def get_ticket(self, id):
        quoted_id = urllib.parse.quote(id, safe="")
        url_suffix = f"/v2/tickets/{quoted_id}"
        try:
            ticket = self._http_request(method="GET", url_suffix=url_suffix)
        except DemistoException as e:
            if e.res is not None and e.res.status_code == 404:
                return None
            else:
                raise

        simplify_ticket(ticket)
        if ticket["notes"]:
            ticket["formatted_notes"] = format_notes(ticket["notes"])
        else:
            ticket["formatted_notes"] = ""
        return ticket

    def add_ticket_comment(self, id, comment):
        quoted_id = urllib.parse.quote(id, safe="")
        url_suffix = f"/v1/tickets/{quoted_id}/comments"
        payload = {
            "comment": comment,
        }
        result = self._http_request(
            method="POST",
            url_suffix=url_suffix,
            json_data=payload,
            empty_valid_codes=(201,),
            return_empty_response=True,
        )
        return result

    def close_ticket(self, id, comment):
        quoted_id = urllib.parse.quote(id, safe="")
        url_suffix = f"/v1/tickets/{quoted_id}/close"
        payload = {
            "comment": comment,
        }
        return self._http_request(
            method="POST",
            url_suffix=url_suffix,
            json_data=payload,
            empty_valid_codes=(202,),
            return_empty_response=True,
        )

    def get_finding(self, id):
        quoted_id = urllib.parse.quote(id, safe="")
        url_suffix = f"/v2/findings/{quoted_id}"
        try:
            finding = self._http_request(method="GET", url_suffix=url_suffix)
        except DemistoException as e:
            if e.res is not None and e.res.status_code == 404:
                return None
            else:
                raise
        simplify_finding(finding)
        return finding

    def get_asset(self, id):
        quoted_id = urllib.parse.quote(id, safe="")
        url_suffix = f"/v2/assets/{quoted_id}"

        try:
            asset = self._http_request(method="GET", url_suffix=url_suffix)
        except DemistoException as e:
            if e.res is not None and e.res.status_code == 404:
                return None
            else:
                raise
        simplify_asset(asset)
        return asset

    def search_assets(self, **kwargs):
        params = {k: v for k, v in kwargs.items() if v is not None}

        url_suffix = "/v2/assets"
        results = self._http_request(method="GET", url_suffix=url_suffix, params=params)

        if "items" not in results:
            return None
        assets = results["items"]
        for asset in assets:
            simplify_asset(asset)

        return assets

    def search_findings(self, **kwargs):
        params = {k: v for k, v in kwargs.items() if v is not None}

        url_suffix = "/v2/findings"
        results = self._http_request(method="GET", url_suffix=url_suffix, params=params)

        if "items" not in results:
            return None
        findings = results["items"]
        for finding in findings:
            simplify_finding(finding)
        return findings


""" HELPER FUNCTIONS """


def arg_to_datestring(arg, arg_name, required=False, format=None):
    dt = arg_to_datetime(arg, arg_name, required)

    datestr = None
    if dt:
        if format is None:
            format = FUSION_DATE_FORMAT
        datestr = dt.strftime(format)
    return datestr


def arg_to_timestamp(arg: Any, arg_name: str, required: bool = False) -> Optional[int]:
    """Converts an XSOAR argument to a timestamp (seconds from epoch)

    This function is used to quickly validate an argument provided to XSOAR
    via ``demisto.args()`` into an ``int`` containing a timestamp (seconds
    since epoch). It will throw a ValueError if the input is invalid.
    If the input is None, it will throw a ValueError if required is ``True``,
    or ``None`` if required is ``False.

    :type arg: ``Any``
    :param arg: argument to convert

    :type arg_name: ``str``
    :param arg_name: argument name

    :type required: ``bool``
    :param required:
        throws exception if ``True`` and argument provided is None

    :return:
        returns an ``int`` containing a timestamp (seconds from epoch) if conversion works
        returns ``None`` if arg is ``None`` and required is set to ``False``
        otherwise throws an Exception
    :rtype: ``Optional[int]``
    """

    if arg is None:
        if required is True:
            raise ValueError(f'Missing "{arg_name}"')
        return None

    if isinstance(arg, str) and arg.isdigit():
        # timestamp is a str containing digits - we just convert it to int
        return int(arg)
    if isinstance(arg, str):
        # we use dateparser to handle strings either in ISO8601 format, or
        # relative time stamps.
        # For example: format 2019-10-23T00:00:00 or "3 days", etc
        date = dateparser.parse(arg, settings={"TIMEZONE": "UTC"})
        if date is None:
            # if d is None it means dateparser failed to parse it
            raise ValueError(f"Invalid date: {arg_name}")

        return int(date.replace(tzinfo=timezone.utc).timestamp())
    if isinstance(arg, int | float):
        # Convert to int if the input is a float
        return int(arg)
    raise ValueError(f'Invalid date: "{arg_name}"')


def format_notes(notes, limit=5):
    """Format notes json from the Fusion API"""
    results = []
    for note in notes:
        actor = note.get("actor", "")
        ts = note.get("timestamp", "")
        text = note.get("text", "")
        results.append(f"{ts} Created by: {actor}")
        results.append("NOTE:")
        for line in text.split("\n"):
            results.append(f"{line}")
        results.append("----------------")
    if results:
        results.pop()
    return "\n".join(results)


""" COMMAND FUNCTIONS """


def test_module(client: Client) -> str:
    """Tests API connectivity and authentication'

    Returning 'ok' indicates that the integration works like it is supposed to.
    Connection to the service is successful.
    Raises exceptions if something goes wrong.

    :type client: ``Client``
    :param Client: client to use

    :return: 'ok' if test passed, anything else will fail the test.
    :rtype: ``str``
    """

    message: str = ""
    try:
        client.describe()
        message = "ok"
    except DemistoException as e:
        if e.res is not None and e.res.status_code == 401:
            message = "Authorization Error: make sure API Key is correctly set"
        else:
            raise
    return message


def fetch_incidents(client, max_results, first_fetch):
    demisto.debug(f"Fetching {max_results}, {first_fetch} {type(first_fetch)}")
    last_run = demisto.getLastRun()
    last_fetch = last_run.get("last_fetch", None)
    demisto.debug(f"last_run: {last_run}")
    if last_fetch is None:
        # if missing, use what provided via first_fetch
        last_fetch = first_fetch
    else:
        # otherwise use the stored last fetch
        last_fetch = int(last_fetch)

    search_since = max(first_fetch, last_fetch)
    demisto.debug(f"{first_fetch} <=> {last_fetch} == {search_since}")
    created_since = timestamp_to_datestring(search_since * 1000, FUSION_DATE_FORMAT)

    ticket_types = demisto.params().get("ticket_types")
    incidents = []
    params = {
        "pageSize": max_results,
        "createdSince": created_since,
        "sortField": "createdOn",
        "sortDescending": "false",
    }
    if ticket_types:
        params["type"] = ",".join(ticket_types)
    demisto.debug(f"params: {params}")
    tickets = client.search_tickets(**params)
    latest_timestamp = search_since
    if tickets:
        demisto.debug(f"Found {len(tickets)} tickets from Fusion")
        for tkt in tickets:
            simplify_ticket(tkt)
            incident = {
                "name": tkt["subject"],
                "occurred": tkt["createdOn"],
                "description": tkt.get("description", ""),
                "severity": SEVERITY_MAP.get(tkt.get("priority"), 0),
                "rawJSON": json.dumps(tkt),
            }
            dt = dateparser.parse(tkt["createdOn"], settings={"TIMEZONE": "UTC"})
            assert dt is not None, f'could not parse {tkt["createdOn"]}'
            latest_timestamp = max(dt.timestamp(), latest_timestamp)
            incidents.append(incident)

    # One second in the future to prevent duplicates
    last_run = {"last_fetch": latest_timestamp + 1}
    demisto.setLastRun(last_run)
    demisto.incidents(incidents)


def simplify_ticket(ticket):
    if not ticket:
        return
    for f in list(ticket.keys()):
        if f not in TICKET_FIELDS:
            del ticket[f]


def simplify_finding(finding):
    if not finding:
        return

    for f in list(finding.keys()):
        if f not in FINDING_FIELDS:
            del finding[f]
    if finding.get("status"):
        finding["status"].pop("code", None)


def simplify_asset(asset):
    if not asset:
        return
    for f in list(asset.keys()):
        if f not in ASSET_FIELDS:
            del asset[f]


def get_ticket_command(client, args):
    id = args.get("id")
    ticket = client.get_ticket(id)

    if ticket is None:
        return CommandResults(readable_output=f"Ticket {id} not found")

    readable = [
        "| field | value |",
        "|-|-|",
    ]
    for k, v in ticket.items():
        str_value = stringEscapeMD(formatCell(v), True, True)
        if len(str_value) > 200:
            str_value = str_value[:200] + "....[Truncated]"
        readable.append(f"| {k} | {str_value} |")

    command_results = CommandResults(
        outputs_prefix="Trustwave.Ticket",
        outputs_key_field="number",
        outputs=ticket,
        readable_output="\n".join(readable),
    )
    return command_results


def add_ticket_comment_command(client, args):
    id = args.get("id")
    comment = args.get("comment")
    client.add_ticket_comment(id, comment)

    return "Success"


def close_ticket_command(client, args):
    id = args.get("id")
    comment = args.get("comment")
    client.close_ticket(id, comment)

    return "Success"


def get_finding_command(client, args):
    id = args.get("id")
    finding = client.get_finding(id)

    if finding:
        # TODO: Add readable_output (markdown) for warroom view
        command_results = CommandResults(outputs_prefix="Trustwave.Finding", outputs_key_field="id", outputs=finding)
    else:
        command_results = CommandResults(readable_output=f"Finding {id} not found")
    return command_results


def get_asset_command(client, args):
    id = args.get("id")
    asset = client.get_asset(id)

    if asset:
        # TODO: Add readable_output (markdown) for warroom view
        command_results = CommandResults(outputs_prefix="Trustwave.Asset", outputs_key_field="id", outputs=asset)
    else:
        command_results = CommandResults(readable_output=f"Asset {id} not found")
    return command_results


def get_updated_tickets_command(client, args):
    updated_since = arg_to_datestring(args.get("since"), arg_name="since", required=True, format=FUSION_DATE_FORMAT)
    ticket_types = args.get("ticket_types", "INCIDENT")
    max_tickets = args.get("fetch_limit", 100)

    demisto.debug(f"Searching since {updated_since}")
    tickets = client.search_tickets(updatedSince=updated_since, type=ticket_types, pageSize=max_tickets)

    if tickets:
        command_results = CommandResults(outputs_prefix="Trustwave.Ticket", outputs_key_field="number", outputs=tickets)
    else:
        command_results = CommandResults(readable_output="No updated tickets found")
    return command_results


def search_findings_command(client, args):
    finding_id = args.get("id")
    limit = args.get("limit", 100)
    name = args.get("name")
    classification = args.get("classification")
    summary = args.get("summary")
    detail = args.get("detail")
    severity = args.get("severity")
    priority = args.get("priority")
    created = arg_to_datestring(args.get("created_since"), arg_name="created_since", required=False, format=FUSION_DATE_FORMAT)
    demisto.debug(f"created: {created}")
    updated = arg_to_datestring(args.get("updated_since"), arg_name="updated_since", required=False, format=FUSION_DATE_FORMAT)
    demisto.debug(f"updated: {updated}")
    findings = None
    if finding_id is not None:
        finding = client.get_finding(finding_id)
        if finding:
            findings = [finding]
    else:
        findings = client.search_findings(
            pageSize=limit,
            name=name,
            classification=classification,
            summary=summary,
            detail=detail,
            priority=priority,
            severity=severity,
            createdSince=created,
            updatedSince=updated,
        )

    if findings:
        command_results = CommandResults(outputs_prefix="Trustwave.Finding", outputs_key_field="id", outputs=findings)
    else:
        command_results = CommandResults(readable_output="No matching findings found")
    return command_results


def search_assets_command(client, args):
    demisto.debug(f"args = {args}")
    asset_id = args.get("id")
    limit = args.get("limit", 100)
    name = args.get("name")
    ips = args.get("ips")
    demisto.debug(f"ips= {ips}")
    os = args.get("os")
    tags = args.get("tags")
    port = args.get("port")
    app_proto = args.get("app_protocol")
    transport = args.get("transport")
    asset_type = args.get("type")
    created = arg_to_datestring(args.get("created_since"), arg_name="created_since", required=False, format=FUSION_DATE_FORMAT)

    updated = arg_to_datestring(args.get("updated_since"), arg_name="updated_since", required=False, format=FUSION_DATE_FORMAT)

    assets = None
    if asset_id is not None:
        asset = client.get_asset(asset_id)
        if asset:
            assets = [asset]
    else:
        assets = client.search_assets(
            pageSize=limit,
            name=name,
            ips=ips,
            os=os,
            tags=tags,
            port=port,
            applicationProtocol=app_proto,
            transportProtocol=transport,
            type=asset_type,
            createdSince=created,
            updatedSince=updated,
        )
    if assets:
        command_results = CommandResults(outputs_prefix="Trustwave.Asset", outputs_key_field="id", outputs=assets)
    else:
        command_results = CommandResults(readable_output="No matching assets found")
    return command_results


def search_tickets_command(client, args):
    demisto.debug(f"args = {args}")
    ticket_id = args.get("id")
    subject = args.get("subject")
    limit = args.get("limit", 100)
    ticket_type = args.get("type")
    status = args.get("status")
    priority = args.get("priority")
    impact = args.get("impact")
    urgency = args.get("urgency")
    created = arg_to_datestring(args.get("created_since"), arg_name="created_since", required=False, format=FUSION_DATE_FORMAT)

    updated = arg_to_datestring(args.get("updated_since"), arg_name="updated_since", required=False, format=FUSION_DATE_FORMAT)

    tickets = None
    if ticket_id is not None:
        ticket = client.get_ticket(ticket_id)
        if ticket:
            tickets = [ticket]
    else:
        tickets = client.search_tickets(
            pageSize=limit,
            type=ticket_type,
            subject=subject,
            status=status,
            priority=priority,
            impact=impact,
            urgency=urgency,
            createdSince=created,
            updatedSince=updated,
        )
    if tickets:
        command_results = CommandResults(outputs_prefix="Trustwave.Ticket", outputs_key_field="id", outputs=tickets)
    else:
        command_results = CommandResults(readable_output="No matching tickets found")
    return command_results


""" MAIN FUNCTION """


def main() -> None:
    """main function, parses params and runs command functions

    :return:
    :rtype:
    """

    api_key = demisto.params().get("apikey")
    base_url = demisto.params()["url"]

    verify_certificate = not demisto.params().get("insecure", False)
    proxy = demisto.params().get("proxy", False)

    first_fetch = arg_to_timestamp(
        arg=demisto.params().get("first_fetch", "3 days"),
        arg_name="First fetch time",
        required=True,
    )
    max_fetch = 100
    try:
        max_fetch = int(demisto.params().get("max_fetch", "100"))
    except ValueError:
        return_error("Maximum number of incidents per fetch needs to be an integer")

    demisto.debug(f"Command being called is {demisto.command()}")
    try:
        headers: dict = {
            "Authorization": f"Bearer {api_key}",
        }

        client = Client(base_url=base_url, verify=verify_certificate, headers=headers, proxy=proxy)

        if demisto.command() == "test-module":
            # This is the call made when pressing the integration Test button.
            result = test_module(client)
            return_results(result)

        elif demisto.command() == "fetch-incidents":
            fetch_incidents(client, max_fetch, first_fetch)
        elif demisto.command() == "trustwave-get-ticket":
            return_results(get_ticket_command(client, demisto.args()))
        elif demisto.command() == "trustwave-add-ticket-comment":
            return_results(add_ticket_comment_command(client, demisto.args()))
        elif demisto.command() == "trustwave-close-ticket":
            return_results(close_ticket_command(client, demisto.args()))
        elif demisto.command() == "trustwave-get-finding":
            return_results(get_finding_command(client, demisto.args()))
        elif demisto.command() == "trustwave-get-updated-tickets":
            return_results(get_updated_tickets_command(client, demisto.args()))
        elif demisto.command() == "trustwave-get-asset":
            return_results(get_asset_command(client, demisto.args()))
        elif demisto.command() == "trustwave-search-assets":
            return_results(search_assets_command(client, demisto.args()))
        elif demisto.command() == "trustwave-search-findings":
            return_results(search_findings_command(client, demisto.args()))
        elif demisto.command() == "trustwave-search-tickets":
            return_results(search_tickets_command(client, demisto.args()))

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


""" ENTRY POINT """


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