QRadar v3
IBM QRadar SIEM helps security teams accurately detect and prioritize threats across the enterprise, supports API versions 10.1 and above. Provides intelligent insights that enable teams to respond quickly to reduce the impact of incidents.
Analytics & SIEM · IBM QRadar
Details
| ID | QRadar v3 |
|---|---|
| Provider | Palo Alto Networks |
| Category | Analytics & SIEM |
| From Version | 5.5.0 |
| Docker Image | demisto/python3:3.12.13.10399583 |
| Supported Modules | Agentix Cloud Runtime Security Cloud Posture Security XSIAM EDR Cortex Cloud |
README
IBM QRadar SIEM helps security teams accurately detect and prioritize threats across the enterprise, supports API versions 10.1 and above. Provides intelligent insights that enable teams to respond quickly to reduce the impact of incidents.
This integration was integrated and tested with version 14-20 of QRadar v3
This is the default integration for this content pack when configured by the Data Onboarder in Cortex XSIAM.
Configure IBM QRadar v3 in Cortex
| Parameter | Description | Required |
|---|---|---|
| Server URL | (e.g., https://1.1.1.1) | True |
| Username | True | |
| Password | True | |
| QRadar API Version | API version of QRadar (e.g., ‘12.0’). Minimum API version is 10.1. | True |
| Incident Type | False | |
| First fetch timestamp (<number> <time unit>, e.g., 12 hours, 7 days) | if no offenses are found within the range of first fetch, will be set to fetch the earliest offense. | False |
| Fetch mode | True | |
| Retry events fetch | Whenever enabled, the integration retries to fetch all events if the number of events fetched is less than `event_count`. Default number of tries is 3, but can be configured via the Advanced Parameter: EVENTS_SEARCH_TRIES. e.g EVENTS_SEARCH_TRIES=5 | False |
| Maximum number of events per incident. | The maximal amount of events to pull per incident. | False |
| Number of offenses to pull per API call (max 50) | In case of mirroring with events, this value will be used for mirroring API calls as well, and it is advised to have a small value. | False |
| Query to fetch offenses. | Define a query to determine which offenses to fetch. E.g., “severity >= 4 AND id > 5”. | False |
| Incidents Enrichment | IPs enrichment transforms IDs of the IPs of the offense to IP values. Asset enrichment adds correlated assets to the fetched offenses. | True |
| Event fields to return from the events query (WARNING: This parameter is correlated to the incoming mapper and changing the values may adversely affect mapping). | The parameter uses the AQL SELECT syntax. For more information, see: https://www.ibm.com/support/knowledgecenter/en/SS42VS_7.4/com.ibm.qradar.doc/c_aql_intro.html. Selecting over 100 fields can result in long waiting times for queries. | False |
| Mirroring Options | How mirroring from QRadar to Cortex XSOAR should be done, available from QRadar 7.3.3 Fix Pack 3. For further explanation on how to check your QRadar version, see the integration documentation at https://xsoar.pan.dev. | False |
| Close Mirrored XSOAR Incident | When selected, closing the QRadar offense is mirrored in Cortex XSOAR. | False |
| The number of incoming incidents to mirror each time | Maximum number of incoming incidents to mirror each time. | False |
| Advanced Parameters | Comma-separated configuration for advanced parameter values. E.g., EVENTS_INTERVAL_SECS=20,FETCH_SLEEP=5 | False |
| Trust any certificate (not secure) | False | |
| Use system proxy settings | False | |
| Timeout for http-requests | The timeout of the HTTP requests sent to the Qradar API (in seconds). If no value is provided, the timeout will be set to 60 seconds. | False |
| Fetch Incidents Interval | The fetch interval between before each fetch-incidents execution. (seconds) | False |
Required Permissions
| Component | Permission |
|---|---|
| Assets | Vulnerability Management or Assets |
| Domains | Admin |
| Offenses (Manage Closing Reason) | Manage Offense Closing Reasons |
| Offenses (Assign Offenses to Users) | Assign Offenses to Users |
| Offenses (Read) | Offenses |
| References (Create/Update) | Admin |
| References (Read) | View Reference Data |
Mapping Limitation for Cortex XSOAR Versions below 6.0.0
The Pull from instance option to create a new mapper is not supported in Cortex XSOAR versions below 6.0.0.
Creating a Classifier Using the Pull from instance Parameter
QRadar fetches incidents using a long-running execution, not in real time. Therefore, Pull from instance pulls incidents from the QRadar service to create a classifier using samples, not real time data. This results in seeing the latest sample stored, and not the latest offense that was fetched.
Important Note Regarding the Query to fetch offenses Parameter
The Query to fetch offenses feature enables defining a specific query for offenses to be retrieved, e.g., ‘status = OPEN and id = 5’. The QRadar integration keeps track of IDs that have already been fetched in order to avoid duplicate fetching.
If you change the Query to fetch offenses value, it will not re-fetch offenses that have already been fetched. To re-fetch those offenses, run the qradar-reset-last-run command.
Note:
The list of QRadar IDs that were already fetched will be reset and duplicate offenses could be re-fetched, depending on the user query.
In case of setting a query with the intent to fetch specific offenses or older offenses, make sure that the First fetch timestamp parameter is set to a value that covers the desired time range for the offenses to be fetched. If using an already existing instance that has performed fetch, run the qradar-reset-last-run command before starting the fetch with the new query.
Migration from QRadar v2 to QRadar v3
Every command and playbook that runs in QRadar v2 also runs in QRadar v3. No adjustments are required.
Additions and Changes from QRadar v2 to QRadar v3
New Commands
- qradar-rule-groups-list
- qradar-searches-list
- qradar-geolocations-for-ip
- qradar-log-sources-list
- qradar-upload-indicators
- get-modified-remote-data
Command Name Changes
| QRadar v2 command | QRadar V3 command | Notes | |
|---|---|---|---|
| qradar-offenses | qradar-offenses-list | ||
| qradar-offense-by-id | qradar-offenses-list | Specify the offense_id argument in the command. | |
| qradar-update-offense | qradar-offense-update | ||
| qradar-get-closing-reasons | qradar-closing-reasons | ||
| qradar-get-note | qradar-offense-notes-list | ||
| qradar-create-note | qradar-offense-note-create | ||
| qradar-get-assets | qradar-assets-list | ||
| qradar-get-asset-by-id | qradar-assets-list | Specify the asset_id argument in the command. | |
| qradar-searches | qradar-search-create | ||
| qradar-get-search | qradar-search-status-get | ||
| qradar-get-search-results | qradar-search-results-get | ||
| qradar-get-reference-by-name | qradar-reference-sets-list | Specify the ref_name argument in the command. | |
| qradar-create-reference-set | qradar-reference-set-create | ||
| qradar-delete-reference-set | qradar-reference-set-delete | ||
| qradar-create-reference-set-value | qradar-reference-set-value-upsert | ||
| qradar-update-reference-set-value | qradar-reference-set-value-upsert | ||
| qradar-delete-reference-set-value | qradar-reference-set-value-delete | ||
| qradar-get-domains | qradar-domains-list | ||
| qradar-get-domain-by-id | qradar-domains-list | Specify the domain_id argument in the command. |
Mirroring
This integration supports in mirroring from QRadar offenses to Cortex XSOAR.
When a field of an offense is updated in QRadar services, the update is mirrored in Cortex XSOAR.
Mirroring Events
- Mirroring events from QRadar to Cortex XSOAR is supported via the Mirror Offense and Events option.
- Events will only be mirrored in the incoming direction.
- Mirroring events will only work when the Long running instance parameter is enabled.
- Filtering events using the events_limit and events_columns options for mirrored incidents will be the same as in the fetched incidents.
- The integration will always mirror the events that occurred first in each offense.
For more information about mirroring configurations, see here.
Use the API Token Instead of Username and Password
- In the Username / API Key field, type _api_token_key.
- In the Password field, type your API token.
Choose Your API Version
- Visit the QRadar API versions page for a full list of available API versions according to the QRadar version.
- Choose one of the API versions listed under the Supported REST API versions column in the line corresponding to your QRadar version.
Note:
If you’re uncertain which API version to use, it is recommended to use the latest API version listed in the Supported REST API versions column in the line corresponding to your QRadar version.
View Your QRadar Version
- Enter QRadar service.
- Click the Menu toolbar. A scrolling toolbar will appear.
- Click About. A new window will appear with the details of your QRadar version.
Troubleshooting
Fetch with Events Retries
When “Fetch with events” is configured, the integration will fetch the offense events from IBM QRadar.
Nevertheless, some events may not be available when trying to fetch them during an incident creation. If Retry events fetch is enabled, the integration tries to fetch more events when the number fetched is less than the expected event_count. In the default setting, the integration will try 3 times, with a wait time of 100 seconds between retries.
In order to change the default values, configure the following Advanced Parameters in the instance configuration:
EVENTS_SEARCH_TRIES=<amount of tries for events search> (default 3),EVENTS_SEARCH_RETRY_SECONDS=<amount of seconds to wait between tries> (default 100),EVENTS_POLLING_TRIES=<number of times to poll for one search> (default 10),
It is recommended to enable mirroring, as it should fetch previously missed events when the offense is updated.
Alternatively, the retrieve events command can be used to retrieve the events immediately.
If the command takes too long to finish executing, try setting the interval_in_seconds to a lower value (down to a minimum of 10 seconds).
API Call Metrics
This feature collects metadata on QRadar API calls and their success status.
API Call metrics are not available for long-running commands such as fetch incidents.
API Metrics are shown in the built-in API Execution Metrics dashboard, and are available to use in custom widgets.
Duplicate Incidents
When running on an Engine Load-Balancing Group, concurrent fetches may run on multiple containers, leading to the creation of duplicate Incidents.
Recommended workarounds:
- Option A - Preserve high availability: Continue running the integration on the Load-Balancing Group for optimal fault tolerance and higher availability. Create an Incident Pre-processing Rule to drop duplicate incidents.
- Option B - Use a simplified setup: Alternatively, run the integration on a single custom engine or use the default “No engine” option if possible for a more streamlined configuration.
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.
qradar-offenses-list
Gets offenses from QRadar.
Base Command
qradar-offenses-list
Input
| Argument Name | Description | Required |
|---|---|---|
| offense_id | The offense ID to retrieve its details. Specify offense_id to get details about a specific offense. | Optional |
| enrichment | IPs enrichment transforms IDs of the IPs of the offense to IP values. Asset enrichment adds correlated assets to the fetched offenses. Possible values are: IPs, IPs And Assets, None. Default is None. | Optional |
| range | Range of results to return (e.g.: 0-20, 3-5, 3-3). Default is 0-49. | Optional |
| filter | Query to filter offenses, e.g., “severity >= 4 AND id > 5 AND status=OPEN”. For reference, see: https://www.ibm.com/support/knowledgecenter/SS42VS_SHR/com.ibm.qradarapi.doc/c_rest_api_filtering.html. | Optional |
| fields | Comma-separated list of fields to retrieve in the response. Fields that are not explicitly named are excluded. E.g., “id,severity,status”. Specify subfields in brackets and multiple fields in the same object separated by commas. For a full list of available fields, see: https://ibmsecuritydocs.github.io/qradar_api_14.0/14.0–siem-offenses-GET.html. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.Offense.Description | String | Description of the offense. |
| QRadar.Offense.Rules.id | Number | The ID of the rule. |
| QRadar.Offense.Rules.type | String | The type of the rule. |
| QRadar.Offense.Rules.name | String | The name of the rule. |
| QRadar.Offense.EventCount | Number | Number of events that are associated with the offense. |
| QRadar.Offense.FlowCount | Number | Number of flows that are associated with the offense. |
| QRadar.Offense.AssignedTo | String | The user to whom the offense is assigned. |
| QRadar.Offense.Followup | Boolean | Whether the offense is marked for follow-up. |
| QRadar.Offense.SourceAddress | Number | Source addresses (IPs if IPs enrich have been requested, else IDs of the IPs) that are associated with the offense. |
| QRadar.Offense.Protected | Boolean | Whether the offense is protected. |
| QRadar.Offense.ClosingUser | String | The user who closed the offense. |
| QRadar.Offense.DestinationHostname | String | Destination networks that are associated with the offense. |
| QRadar.Offense.CloseTime | Date | Time when the offense was closed. |
| QRadar.Offense.RemoteDestinationCount | Number | Number of remote destinations that are associated with the offense. |
| QRadar.Offense.StartTime | Date | Date of the earliest item that contributed to the offense. |
| QRadar.Offense.Magnitude | Number | Magnitude of the offense. |
| QRadar.Offense.LastUpdatedTime | String | Date of the most recent item that contributed to the offense. |
| QRadar.Offense.Credibility | Number | Credibility of the offense. |
| QRadar.Offense.ID | Number | ID of the offense. |
| QRadar.Offense.Categories | String | Event categories that are associated with the offense. |
| QRadar.Offense.Severity | Number | Severity of the offense. |
| QRadar.Offense.ClosingReason | String | Reason the offense was closed. |
| QRadar.Offense.OffenseType | String | Type of the offense. |
| QRadar.Offense.Relevance | Number | Relevance of the offense. |
| QRadar.Offense.OffenseSource | String | Source of the offense. |
| QRadar.Offense.DestinationAddress | Number | Destination addresses (IPs if IPs enrichment have been requested, else IDs of the IPs) that are associated with the offense. |
| QRadar.Offense.Status | String | Status of the offense. Possible values: “OPEN”, “HIDDEN”, “CLOSED”. |
| QRadar.Offense.LinkToOffense | String | Link to the URL containing information about the offense. |
| QRadar.Offense.Assets | String | Assets correlated to the offense, if enrichment was requested. |
Command example
!qradar-offenses-list enrichment=IPs filter="status=OPEN" range=0-2
Context Example
{
"QRadar": {
"Offense": [
{
"AssignedTo": "admin",
"Categories": [
"Information",
"Suspicious Activity",
"Process Creation Success",
"Suspicious Windows Events",
"User Login Attempt",
"Misc Login Succeeded",
"Virtual Machine Creation Attempt",
"Read Activity Attempted",
"Object Download Attempt"
],
"Credibility": 4,
"Description": "Detected A Successful Login From Different Geographies For the Same Username - AWSCloud (Exp Center)\n preceded by An AWS API Has Been Invoked From Kali - AWSCloud (Exp Center)\n preceded by Microsoft Word Launc\n preceded by Detected a Massive Creation of EC2 Instances - AWSCloud (Exp Center)\n containing Mail Server Info Message\n",
"DestinationAddress": [
"1.1.1.1",
"1.1.1.1"
],
"DestinationHostname": [
"other",
"Net-10-172-192.Net_192_168_0_0"
],
"EventCount": 35651,
"FlowCount": 0,
"Followup": true,
"ID": 14,
"LastUpdatedTime": "2023-07-26T15:31:11.839000+00:00",
"LinkToOffense": "https://ec2.eu.compute-1.amazonaws.com/console/do/sem/offensesummary?appName=Sem&pageId=OffenseSummary&summaryId=14",
"Magnitude": 4,
"OffenseSource": "userD",
"OffenseType": "Username",
"Protected": false,
"Relevance": 0,
"RemoteDestinationCount": 1,
"Rules": [
{
"id": 102539,
"name": "EC: AWS Cloud - Detected An Email with An Attachment From a Spam Sender",
"type": "CRE_RULE"
},
{
"id": 102589,
"name": "EC: AWS Cloud - Microsoft Word Launched a Command Shell",
"type": "CRE_RULE"
},
{
"id": 102639,
"name": "EC: AWS Cloud - Detected A Successful Login From Different Geographies For the Same Username",
"type": "CRE_RULE"
},
{
"id": 102389,
"name": "EC: AWS Cloud - An AWS API Has Been Invoked From Kali",
"type": "CRE_RULE"
},
{
"id": 102439,
"name": "EC: AWS Cloud - A Database backup Has Been Downloaded From S3 Bucket",
"type": "CRE_RULE"
},
{
"id": 102489,
"name": "EC: AWS Cloud - Detected a Massive Creation of EC2 Instances",
"type": "CRE_RULE"
}
],
"Severity": 10,
"SourceAddress": [
"1.1.1.1",
"1.1.1.1",
"1.1.1.1",
"1.1.1.1"
],
"StartTime": "2023-07-26T14:31:13.387000+00:00",
"Status": "OPEN"
},
{
"Categories": [
"Mail",
"System Failure"
],
"Credibility": 2,
"Description": "Flow Source/Interface Stopped Sending Flows\n",
"DestinationAddress": [
"1.1.1.1"
],
"DestinationHostname": [
"Net-10-172-192.Net_10_0_0_0"
],
"EventCount": 2,
"FlowCount": 6026,
"Followup": true,
"ID": 13,
"LastUpdatedTime": "2023-06-12T08:49:50.145000+00:00",
"LinkToOffense": "https://ec2.eu.compute-1.amazonaws.com/console/do/sem/offensesummary?appName=Sem&pageId=OffenseSummary&summaryId=13",
"Magnitude": 1,
"OffenseSource": "Flow Source Stopped Sending Flows",
"OffenseType": "Rule",
"Protected": true,
"Relevance": 0,
"RemoteDestinationCount": 0,
"Rules": [
{
"id": 100270,
"name": "Flow Source Stopped Sending Flows",
"type": "CRE_RULE"
}
],
"Severity": 1,
"SourceAddress": [
"1.1.1.1",
"1.1.1.1",
"1.1.1.1"
],
"StartTime": "2023-06-12T08:19:02.020000+00:00",
"Status": "OPEN"
},
{
"Categories": [
"User Activity"
],
"Credibility": 3,
"Description": "User Account Created and Used and Deleted within a short time frame (Exp Center)\n",
"DestinationAddress": [
"1.1.1.1"
],
"DestinationHostname": [
"Net-10-172-192.Net_172_16_0_0"
],
"EventCount": 8,
"FlowCount": 0,
"Followup": true,
"ID": 12,
"LastUpdatedTime": "2023-06-12T08:17:33.008000+00:00",
"LinkToOffense": "https://ec2.eu.compute-1.amazonaws.com/console/do/sem/offensesummary?appName=Sem&pageId=OffenseSummary&summaryId=12",
"Magnitude": 2,
"OffenseSource": "badadmin",
"OffenseType": "Username",
"Protected": true,
"Relevance": 0,
"RemoteDestinationCount": 0,
"Rules": [
{
"id": 102989,
"name": "EC: User Account Created and Used and Removed",
"type": "CRE_RULE"
}
],
"Severity": 5,
"SourceAddress": [
"1.1.1.1"
],
"StartTime": "2023-06-12T08:15:54.740000+00:00",
"Status": "OPEN"
}
]
}
}
Human Readable Output
Offenses List
ID Description OffenseType Status Severity DestinationHostname LastUpdatedTime Credibility Rules SourceAddress AssignedTo OffenseSource Followup EventCount StartTime FlowCount DestinationAddress LinkToOffense RemoteDestinationCount Relevance Categories Magnitude Protected 14 Detected A Successful Login From Different Geographies For the Same Username - AWSCloud (Exp Center)
preceded by An AWS API Has Been Invoked From Kali - AWSCloud (Exp Center)
preceded by Microsoft Word Launc
preceded by Detected a Massive Creation of EC2 Instances - AWSCloud (Exp Center)
containing Mail Server Info MessageUsername OPEN 10 other,
Net-10-172-192.Net_192_168_0_02023-07-26T15:31:11.839000+00:00 4 {‘id’: 102539, ‘type’: ‘CRE_RULE’, ‘name’: ‘EC: AWS Cloud - Detected An Email with An Attachment From a Spam Sender’},
{‘id’: 102589, ‘type’: ‘CRE_RULE’, ‘name’: ‘EC: AWS Cloud - Microsoft Word Launched a Command Shell’},
{‘id’: 102639, ‘type’: ‘CRE_RULE’, ‘name’: ‘EC: AWS Cloud - Detected A Successful Login From Different Geographies For the Same Username’},
{‘id’: 102389, ‘type’: ‘CRE_RULE’, ‘name’: ‘EC: AWS Cloud - An AWS API Has Been Invoked From Kali’},
{‘id’: 102439, ‘type’: ‘CRE_RULE’, ‘name’: ‘EC: AWS Cloud - A Database backup Has Been Downloaded From S3 Bucket’},
{‘id’: 102489, ‘type’: ‘CRE_RULE’, ‘name’: ‘EC: AWS Cloud - Detected a Massive Creation of EC2 Instances’}1.1.1.1,
1.1.1.1,
1.1.1.1,
1.1.1.1admin userD true 35651 2023-07-26T14:31:13.387000+00:00 0 1.1.1.1,
1.1.1.1https://ec2.eu.compute-1.amazonaws.com/console/do/sem/offensesummary?appName=Sem&pageId=OffenseSummary&summaryId=14 1 0 Information,
Suspicious Activity,
Process Creation Success,
Suspicious Windows Events,
User Login Attempt,
Misc Login Succeeded,
Virtual Machine Creation Attempt,
Read Activity Attempted,
Object Download Attempt4 false 13 Flow Source/Interface Stopped Sending Flows Rule OPEN 1 Net-10-172-192.Net_10_0_0_0 2023-06-12T08:49:50.145000+00:00 2 {‘id’: 100270, ‘type’: ‘CRE_RULE’, ‘name’: ‘Flow Source Stopped Sending Flows’} 1.1.1.1,
1.1.1.1,
1.1.1.1Flow Source Stopped Sending Flows true 2 2023-06-12T08:19:02.020000+00:00 6026 1.1.1.1 https://ec2-1.eu.compute-1.amazonaws.com/console/do/sem/offensesummary?appName=Sem&pageId=OffenseSummary&summaryId=13 0 0 Mail,
System Failure1 true 12 User Account Created and Used and Deleted within a short time frame (Exp Center) Username OPEN 5 Net-10-172-192.Net_172_16_0_0 2023-06-12T08:17:33.008000+00:00 3 {‘id’: 102989, ‘type’: ‘CRE_RULE’, ‘name’: ‘EC: User Account Created and Used and Removed’} 1.1.1.1 badadmin true 8 2023-06-12T08:15:54.740000+00:00 0 1.1.1.1 https://ec2-3.eu.compute-1.amazonaws.com/console/do/sem/offensesummary?appName=Sem&pageId=OffenseSummary&summaryId=12 0 0 User Activity 2 true
qradar-offense-update
Updates an offense.
Base Command
qradar-offense-update
Input
| Argument Name | Description | Required |
|---|---|---|
| offense_id | The ID of the offense to update. | Required |
| enrichment | IPs enrichment transforms IDs of the IPs of the offense to IP values. Asset enrichment adds correlated assets to the fetched offenses. Possible values are: IPs, IPs And Assets, None. Default is None. | Optional |
| protected | Whether the offense should be protected. Possible values are: true, false. | Optional |
| follow_up | Whether the offense should be marked for follow-up. Possible values are: true, false. | Optional |
| status | The new status for the offense. When the status of an offense is set to CLOSED, a valid closing_reason_id must be provided. To hide an offense, use the HIDDEN status. To show a previously hidden offense, use the OPEN status. Possible values are: OPEN, HIDDEN, CLOSED. | Optional |
| closing_reason_id | The ID of a closing reason. You must provide a valid closing_reason_id when you close an offense. For a full list of closing reason IDs, use the ‘qradar-closing-reasons’ command. | Optional |
| closing_reason_name | The name of a closing reason. You must provide a valid closing_reason_name when you close an offense. The default closing_reasons are: (1) False-Positive, Tuned (2) Non-Issues (3) Policy Violation. | Optional |
| assigned_to | User to assign the offense to. | Optional |
| fields | Comma-separated list of fields to retrieve in the response. Fields that are not explicitly named are excluded. E.g., “id,severity,status”. Specify subfields in brackets and multiple fields in the same object separated by commas. For a full list of available fields, see: https://ibmsecuritydocs.github.io/qradar_api_14.0/14.0–siem-offenses-offense_id-POST.html. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.Offense.Description | String | Description of the offense. |
| QRadar.Offense.Rules.id | Number | The ID of the rule. |
| QRadar.Offense.Rules.type | String | The type of the rule. |
| QRadar.Offense.Rules.name | String | The name of the rule. |
| QRadar.Offense.EventCount | Number | Number of events that are associated with the offense. |
| QRadar.Offense.FlowCount | Number | Number of flows that are associated with the offense. |
| QRadar.Offense.AssignedTo | String | The user to whom the offense is assigned. |
| QRadar.Offense.Followup | Boolean | Whether the offense is marked for follow-up. |
| QRadar.Offense.SourceAddress | Number | Source addresses (IPs if IPs enrich have been requested, else IDs of the IPs) that are associated with the offense. |
| QRadar.Offense.Protected | Boolean | Whether the offense is protected. |
| QRadar.Offense.ClosingUser | String | The user who closed the offense. |
| QRadar.Offense.DestinationHostname | String | Destination networks that are associated with the offense. |
| QRadar.Offense.CloseTime | Date | Time when the offense was closed. |
| QRadar.Offense.RemoteDestinationCount | Number | Number of remote destinations that are associated with the offense. |
| QRadar.Offense.StartTime | Date | Date of the earliest item that contributed to the offense. |
| QRadar.Offense.Magnitude | Number | Magnitude of the offense. |
| QRadar.Offense.LastUpdatedTime | String | Date of the most recent item that contributed to the offense. |
| QRadar.Offense.Credibility | Number | Credibility of the offense. |
| QRadar.Offense.ID | Number | ID of the offense. |
| QRadar.Offense.Categories | String | Event categories that are associated with the offense. |
| QRadar.Offense.Severity | Number | Severity of the offense. |
| QRadar.Offense.ClosingReason | String | Reason the offense was closed. |
| QRadar.Offense.OffenseType | String | Type of the offense. |
| QRadar.Offense.Relevance | Number | Relevance of the offense. |
| QRadar.Offense.OffenseSource | String | Source of the offense. |
| QRadar.Offense.DestinationAddress | Number | Destination addresses (IPs if IPs enrichment have been requested, else IDs of the IPs) that are associated with the offense. |
| QRadar.Offense.Status | String | Status of the offense. Possible values: “OPEN”, “HIDDEN”, “CLOSED”. |
| QRadar.Offense.LinkToOffense | String | Link to the URL containing information about the offense. |
| QRadar.Offense.Assets | String | Assets correlated to the offense, if enrichment was requested. |
Command example
!qradar-offense-update offense_id=14 assigned_to=admin enrichment="IPs And Assets" follow_up=true status=OPEN protected=false
Context Example
{
"QRadar": {
"Offense": {
"AssignedTo": "admin",
"Categories": [
"Information",
"Suspicious Activity",
"Process Creation Success",
"Suspicious Windows Events",
"User Login Attempt",
"Misc Login Succeeded",
"Virtual Machine Creation Attempt",
"Read Activity Attempted",
"Object Download Attempt"
],
"Credibility": 4,
"Description": "Detected A Successful Login From Different Geographies For the Same Username - AWSCloud (Exp Center)\n preceded by An AWS API Has Been Invoked From Kali - AWSCloud (Exp Center)\n preceded by Microsoft Word Launc\n preceded by Detected a Massive Creation of EC2 Instances - AWSCloud (Exp Center)\n containing Mail Server Info Message\n",
"DestinationAddress": [
"1.1.1.1",
"1.1.1.1"
],
"DestinationHostname": [
"other",
"Net-10-172-192.Net_192_168_0_0"
],
"EventCount": 35651,
"FlowCount": 0,
"Followup": true,
"ID": 14,
"LastUpdatedTime": "2023-07-26T15:31:11.839000+00:00",
"LinkToOffense": "https://ec2-1.eu.compute-1.amazonaws.com/console/do/sem/offensesummary?appName=Sem&pageId=OffenseSummary&summaryId=14",
"Magnitude": 4,
"OffenseSource": "userD",
"OffenseType": "Username",
"Protected": false,
"Relevance": 0,
"RemoteDestinationCount": 1,
"Rules": [
{
"id": 102539,
"name": "EC: AWS Cloud - Detected An Email with An Attachment From a Spam Sender",
"type": "CRE_RULE"
},
{
"id": 102589,
"name": "EC: AWS Cloud - Microsoft Word Launched a Command Shell",
"type": "CRE_RULE"
},
{
"id": 102639,
"name": "EC: AWS Cloud - Detected A Successful Login From Different Geographies For the Same Username",
"type": "CRE_RULE"
},
{
"id": 102389,
"name": "EC: AWS Cloud - An AWS API Has Been Invoked From Kali",
"type": "CRE_RULE"
},
{
"id": 102439,
"name": "EC: AWS Cloud - A Database backup Has Been Downloaded From S3 Bucket",
"type": "CRE_RULE"
},
{
"id": 102489,
"name": "EC: AWS Cloud - Detected a Massive Creation of EC2 Instances",
"type": "CRE_RULE"
}
],
"Severity": 10,
"SourceAddress": [
"1.1.1.1",
"1.1.1.1",
"1.1.1.1",
"1.1.1.1"
],
"StartTime": "2023-07-26T14:31:13.387000+00:00",
"Status": "OPEN"
}
}
}
Human Readable Output
offense Update
ID Description OffenseType Status Severity DestinationHostname LastUpdatedTime Credibility Rules SourceAddress AssignedTo OffenseSource Followup EventCount StartTime FlowCount DestinationAddress LinkToOffense RemoteDestinationCount Relevance Categories Magnitude Protected 14 Detected A Successful Login From Different Geographies For the Same Username - AWSCloud (Exp Center)
preceded by An AWS API Has Been Invoked From Kali - AWSCloud (Exp Center)
preceded by Microsoft Word Launc
preceded by Detected a Massive Creation of EC2 Instances - AWSCloud (Exp Center)
containing Mail Server Info MessageUsername OPEN 10 other,
Net-10-172-192.Net_192_168_0_02023-07-26T15:31:11.839000+00:00 4 {‘id’: 102539, ‘type’: ‘CRE_RULE’, ‘name’: ‘EC: AWS Cloud - Detected An Email with An Attachment From a Spam Sender’},
{‘id’: 102589, ‘type’: ‘CRE_RULE’, ‘name’: ‘EC: AWS Cloud - Microsoft Word Launched a Command Shell’},
{‘id’: 102639, ‘type’: ‘CRE_RULE’, ‘name’: ‘EC: AWS Cloud - Detected A Successful Login From Different Geographies For the Same Username’},
{‘id’: 102389, ‘type’: ‘CRE_RULE’, ‘name’: ‘EC: AWS Cloud - An AWS API Has Been Invoked From Kali’},
{‘id’: 102439, ‘type’: ‘CRE_RULE’, ‘name’: ‘EC: AWS Cloud - A Database backup Has Been Downloaded From S3 Bucket’},
{‘id’: 102489, ‘type’: ‘CRE_RULE’, ‘name’: ‘EC: AWS Cloud - Detected a Massive Creation of EC2 Instances’}1.1.1.1,
1.1.1.1,
1.1.1.1,
1.1.1.1admin userD true 35651 2023-07-26T14:31:13.387000+00:00 0 1.1.1.1,
1.1.1.1https://ec2-1.eu.compute-1.amazonaws.com/console/do/sem/offensesummary?appName=Sem&pageId=OffenseSummary&summaryId=14 1 0 Information,
Suspicious Activity,
Process Creation Success,
Suspicious Windows Events,
User Login Attempt,
Misc Login Succeeded,
Virtual Machine Creation Attempt,
Read Activity Attempted,
Object Download Attempt4 false
qradar-closing-reasons
Retrieves a list of offense closing reasons.
Base Command
qradar-closing-reasons
Input
| Argument Name | Description | Required |
|---|---|---|
| closing_reason_id | The closing reason ID for which to retrieve its details. Specify closing_reason_id to get details about a specific closing reason. | Optional |
| include_reserved | If true, reserved closing reasons are included in the response. Possible values are: true, false. Default is false. | Optional |
| include_deleted | If true, deleted closing reasons are included in the response. Possible values are: true, false. Default is false. | Optional |
| range | Range of results to return (e.g.: 0-20, 3-5, 3-3). Default is 0-49. | Optional |
| filter | Query to filter closing reasons, e.g. “id > 5”. For reference see: https://www.ibm.com/support/knowledgecenter/SS42VS_SHR/com.ibm.qradarapi.doc/c_rest_api_filtering.html. | Optional |
| fields | Comma-separated list of fields to retrieve in the response. Fields that are not explicitly named are excluded. E.g., “id,text”. Specify subfields in brackets and multiple fields in the same object separated by commas. For a full list of available fields, see: https://ibmsecuritydocs.github.io/qradar_api_14.0/14.0–siem-offense_closing_reasons-GET.html. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.Offense.ClosingReasons.IsDeleted | Boolean | Whether the closing reason is deleted. Deleted closing reasons cannot be used to close an offense. |
| QRadar.Offense.ClosingReasons.IsReserved | Boolean | Whether the closing reason is reserved. Reserved closing reasons cannot be used to close an offense. |
| QRadar.Offense.ClosingReasons.Name | String | Name of the closing reason. |
| QRadar.Offense.ClosingReasons.ID | Number | ID of the closing reason. |
Command example
!qradar-closing-reasons include_deleted=true include_reserved=true
Context Example
{
"QRadar": {
"Offense": {
"ClosingReasons": [
{
"ID": 2,
"IsDeleted": false,
"IsReserved": false,
"Name": "False-Positive, Tuned"
},
{
"ID": 1,
"IsDeleted": false,
"IsReserved": false,
"Name": "Non-Issue"
},
{
"ID": 3,
"IsDeleted": false,
"IsReserved": false,
"Name": "Policy Violation"
},
{
"ID": 4,
"IsDeleted": false,
"IsReserved": true,
"Name": "System Change (Upgrade, Reset, etc.)"
}
]
}
}
}
Human Readable Output
Closing Reasons
ID Name IsReserved IsDeleted 2 False-Positive, Tuned false false 1 Non-Issue false false 3 Policy Violation false false 4 System Change (Upgrade, Reset, etc.) true false
qradar-offense-notes-list
Retrieves a list of notes for an offense.
Base Command
qradar-offense-notes-list
Input
| Argument Name | Description | Required |
|---|---|---|
| offense_id | The offense ID to retrieve the notes for. | Required |
| note_id | The note ID for which to retrieve its details. Specify note_id to get details about a specific note. | Optional |
| range | Range of results to return (e.g.: 0-20, 3-5, 3-3). Default is 0-49. | Optional |
| filter | Query to filter offense notes, e.g., “username=admin”. For reference, see: https://www.ibm.com/support/knowledgecenter/SS42VS_SHR/com.ibm.qradarapi.doc/c_rest_api_filtering.html. | Optional |
| fields | Comma-separated list of fields to retrieve in the response. Fields that are not explicitly named are excluded. E.g., “username,note_text”. Specify subfields in brackets and multiple fields in the same object separated by commas. For a full list of available fields, see: https://ibmsecuritydocs.github.io/qradar_api_14.0/14.0–siem-offenses-offense_id-notes-GET.html. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.Note.Text | String | The text of the note. |
| QRadar.Note.CreateTime | Date | Creation date of the note. |
| QRadar.Note.ID | Number | ID of the note. |
| QRadar.Note.CreatedBy | String | The user who created the note. |
Command example
!qradar-offense-notes-list offense_id=14 filter="username='API_user: admin'" range=0-1
Context Example
{
"QRadar": {
"Note": [
{
"CreateTime": "2023-07-27T13:58:46.428000+00:00",
"CreatedBy": "API_user: admin",
"ID": 53,
"Text": "Note Regarding The Offense"
},
{
"CreateTime": "2023-08-02T08:23:05.473000+00:00",
"CreatedBy": "API_user: admin",
"ID": 60,
"Text": "Note Regarding The Offense"
}
]
}
}
Human Readable Output
Offense Notes List For Offense ID 14
ID Text CreatedBy CreateTime 53 Note Regarding The Offense API_user: admin 2023-07-27T13:58:46.428000+00:00 60 Note Regarding The Offense API_user: admin 2023-08-02T08:23:05.473000+00:00
qradar-offense-note-create
Creates a note on an offense.
Base Command
qradar-offense-note-create
Input
| Argument Name | Description | Required |
|---|---|---|
| offense_id | The offense ID to add the note to. | Required |
| note_text | The text of the note. | Required |
| fields | Comma-separated list of fields to retrieve in the response. Fields that are not explicitly named are excluded. E.g., “username,note_text”. Specify subfields in brackets and multiple fields in the same object separated by commas. For a full list of available fields, see: https://ibmsecuritydocs.github.io/qradar_api_14.0/14.0–siem-offenses-offense_id-notes-POST.html. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.Note.Text | String | The text of the note. |
| QRadar.Note.CreateTime | Date | Creation date of the note. |
| QRadar.Note.ID | Number | ID of the note. |
| QRadar.Note.CreatedBy | String | The user who created the note. |
Command example
!qradar-offense-note-create note_text="Note Regarding The Offense" offense_id=14
Context Example
{
"QRadar": {
"Note": {
"CreateTime": "2023-08-02T08:39:15.813000+00:00",
"CreatedBy": "API_user: admin",
"ID": 65,
"Text": "Note Regarding The Offense"
}
}
}
Human Readable Output
Create Note
ID Text CreatedBy CreateTime 65 Note Regarding The Offense API_user: admin 2023-08-02T08:39:15.813000+00:00
qradar-rules-list
Retrieves a list of rules.
Base Command
qradar-rules-list
Input
| Argument Name | Description | Required |
|---|---|---|
| rule_id | The rule ID for which to retrieve its details. Specify rule_id to get details about a specific rule. | Optional |
| rule_type | Retrieves rules corresponding to the specified rule type. Possible values are: EVENT, FLOW, COMMON, USER. | Optional |
| range | Range of results to return (e.g.: 0-20, 3-5, 3-3). Default is 0-49. | Optional |
| filter | Query by which to filter rules, e.g., “type=EVENT”. For reference, see: https://www.ibm.com/support/knowledgecenter/SS42VS_SHR/com.ibm.qradarapi.doc/c_rest_api_filtering.html. | Optional |
| fields | Comma-separated list of fields to retrieve in the response. Fields that are not explicitly named are excluded. E.g., “owner,identifier,origin”. Specify subfields in brackets and multiple fields in the same object separated by commas. For a full list of available fields, see: https://ibmsecuritydocs.github.io/qradar_api_14.0/14.0–analytics-rules-GET.html. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.Rule.Owner | String | Owner of the rule. |
| QRadar.Rule.BaseHostID | Number | ID of the host from which the rule’s base capacity was determined. |
| QRadar.Rule.CapacityTimestamp | Number | Date when the rule’s capacity values were last updated. |
| QRadar.Rule.Origin | String | Origin of the rule. Possible values: “SYSTEM”, “OVERRIDE”, “USER”. |
| QRadar.Rule.CreationDate | Date | Date when rule was created. |
| QRadar.Rule.Type | String | Type of the rule. Possible values: “EVENT”, “FLOW”, “COMMON”, “USER”. |
| QRadar.Rule.Enabled | Boolean | Whether rule is enabled. |
| QRadar.Rule.ModificationDate | Date | Date when the rule was last modified. |
| QRadar.Rule.Name | String | Name of the rule. |
| QRadar.Rule.AverageCapacity | Number | Moving average capacity in EPS of the rule across all hosts. |
| QRadar.Rule.ID | Number | ID of the rule. |
| QRadar.Rule.BaseCapacity | Number | Base capacity of the rule in events per second. |
Command example
!qradar-rules-list rule_type=COMMON range=0-2
Context Example
{
"QRadar": {
"Rule": [
{
"AverageCapacity": 0,
"BaseCapacity": 0,
"BaseHostID": 0,
"CapacityTimestamp": 0,
"CreationDate": "2007-10-14T20:12:00.374000+00:00",
"Enabled": true,
"ID": 100068,
"ModificationDate": "2022-11-21T18:44:32.696000+00:00",
"Name": "Login Successful After Scan Attempt",
"Origin": "SYSTEM",
"Owner": "admin",
"Type": "COMMON"
},
{
"AverageCapacity": 0,
"BaseCapacity": 0,
"BaseHostID": 0,
"CapacityTimestamp": 0,
"CreationDate": "2006-03-27T10:54:12.077000+00:00",
"Enabled": false,
"ID": 100102,
"ModificationDate": "2023-02-23T14:12:52.067000+00:00",
"Name": "Potential Botnet Connection (DNS)",
"Origin": "SYSTEM",
"Owner": "admin",
"Type": "COMMON"
},
{
"AverageCapacity": 0,
"BaseCapacity": 0,
"BaseHostID": 0,
"CapacityTimestamp": 0,
"CreationDate": "2005-12-22T00:54:48.708000+00:00",
"Enabled": true,
"ID": 100109,
"ModificationDate": "2023-02-23T14:12:49.992000+00:00",
"Name": "Host Port Scan Detected by Remote Host",
"Origin": "SYSTEM",
"Owner": "admin",
"Type": "COMMON"
}
]
}
}
Human Readable Output
Rules List
ID Name Type Enabled BaseHostID Origin ModificationDate CreationDate BaseCapacity AverageCapacity Owner CapacityTimestamp 100068 Login Successful After Scan Attempt COMMON true 0 SYSTEM 2022-11-21T18:44:32.696000+00:00 2007-10-14T20:12:00.374000+00:00 0 0 admin 0 100102 Potential Botnet Connection (DNS) COMMON false 0 SYSTEM 2023-02-23T14:12:52.067000+00:00 2006-03-27T10:54:12.077000+00:00 0 0 admin 0 100109 Host Port Scan Detected by Remote Host COMMON true 0 SYSTEM 2023-02-23T14:12:49.992000+00:00 2005-12-22T00:54:48.708000+00:00 0 0 admin 0
qradar-rule-groups-list
Retrieves a list of the rule groups.
Base Command
qradar-rule-groups-list
Input
| Argument Name | Description | Required |
|---|---|---|
| rule_group_id | The rule group ID for which to retrieve its details. Specify rule_group_id to get details about a specific rule group. | Optional |
| range | Range of results to return (e.g.: 0-20, 3-5, 3-3). Default is 0-49. | Optional |
| filter | Query by which to filter rules, e.g., “id >= 125”. For reference, see: https://www.ibm.com/support/knowledgecenter/SS42VS_SHR/com.ibm.qradarapi.doc/c_rest_api_filtering.html. | Optional |
| fields | Comma-separated list of fields to retrieve in the response. Fields that are not explicitly named are excluded. E.g., “owner,parent_id”. Specify subfields in brackets and multiple fields in the same object separated by commas. For a full list of available fields, see: https://ibmsecuritydocs.github.io/qradar_api_14.0/14.0–analytics-rule_groups-GET.html. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.RuleGroup.Owner | String | Owner of the group. |
| QRadar.RuleGroup.ModifiedTime | Date | Date since the group was last modified. |
| QRadar.RuleGroup.Level | Number | Depth of the group in the group hierarchy. |
| QRadar.RuleGroup.Name | String | Name of the group. |
| QRadar.RuleGroup.Description | String | Description of the group. |
| QRadar.RuleGroup.ID | Number | ID of the group. |
| QRadar.RuleGroup.ChildItems | String | Child items of the group. |
| QRadar.RuleGroup.ChildGroups | Number | Child group IDs. |
| QRadar.RuleGroup.Type | String | The type of the group. |
| QRadar.RuleGroup.ParentID | Number | ID of the parent group. |
Command example
!qradar-rule-groups-list range=0-2
Context Example
{
"QRadar": {
"RuleGroup": [
{
"ChildItems": [
"100045",
"100046",
"100047",
"100048",
"100049",
"100050",
"100051",
"100052",
"100053",
"100054",
"100055",
"100056",
"1607",
"1608",
"1609",
"1610",
"1611",
"1612",
"1613",
"1614",
"1615",
"1616",
"1617",
"1618"
],
"Description": "Rules focused on detection of suspicious asset reconciliation behavior.",
"ID": 125,
"Level": 2,
"ModifiedTime": "2014-01-06T15:23:26.060000+00:00",
"Name": "Asset Reconciliation Exclusion",
"Owner": "admin",
"ParentID": 3,
"Type": "RULE_GROUP"
},
{
"ChildItems": [
"100057",
"100059"
],
"Description": "Rules that indicate post-intrusion access activity",
"ID": 100020,
"Level": 2,
"ModifiedTime": "2015-07-08T20:14:12.250000+00:00",
"Name": "Horizontal Movement",
"Owner": "admin",
"ParentID": 3,
"Type": "RULE_GROUP"
},
{
"ChildItems": [
"100001",
"100003",
"100044",
"100323",
"1219",
"1265",
"1335",
"1410",
"1411",
"1412",
"1431",
"1443",
"1460",
"1461",
"1471",
"1481",
"1509",
"1552",
"1566"
],
"Description": "Rules based on log source and event anomalies such as high event rates or excessive connections.",
"ID": 101,
"Level": 1,
"ModifiedTime": "2010-08-21T11:48:27.850000+00:00",
"Name": "Anomaly",
"Owner": "admin",
"ParentID": 3,
"Type": "RULE_GROUP"
}
]
}
}
Human Readable Output
Rules Group List
ID Name Description Owner ModifiedTime ParentID Type ChildItems Level 125 Asset Reconciliation Exclusion Rules focused on detection of suspicious asset reconciliation behavior. admin 2014-01-06T15:23:26.060000+00:00 3 RULE_GROUP 100045,
100046,
100047,
100048,
100049,
100050,
100051,
100052,
100053,
100054,
100055,
100056,
1607,
1608,
1609,
1610,
1611,
1612,
1613,
1614,
1615,
1616,
1617,
16182 100020 Horizontal Movement Rules that indicate post-intrusion access activity admin 2015-07-08T20:14:12.250000+00:00 3 RULE_GROUP 100057,
1000592 101 Anomaly Rules based on log source and event anomalies such as high event rates or excessive connections. admin 2010-08-21T11:48:27.850000+00:00 3 RULE_GROUP 100001,
100003,
100044,
100323,
1219,
1265,
1335,
1410,
1411,
1412,
1431,
1443,
1460,
1461,
1471,
1481,
1509,
1552,
15661
qradar-assets-list
Retrieves assets list.
Base Command
qradar-assets-list
Input
| Argument Name | Description | Required |
|---|---|---|
| asset_id | The asset ID for which to retrieve its details. Specify asset_id to get details about a specific asset. | Optional |
| range | Range of results to return (e.g.: 0-20, 3-5, 3-3). Default is 0-49. | Optional |
| filter | Query by which to filter assets, e.g., “domain_id=0”. For reference, see: https://www.ibm.com/support/knowledgecenter/SS42VS_SHR/com.ibm.qradarapi.doc/c_rest_api_filtering.html. | Optional |
| fields | Comma-separated list of fields to retrieve in the response. Fields that are not explicitly named are excluded. E.g., “id,interfaces,users,properties”. Specify subfields in brackets and multiple fields in the same object separated by commas. For a full list of available fields, see: https://ibmsecuritydocs.github.io/qradar_api_14.0/14.0–asset_model-assets-GET.html. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Endpoint.Domain | String | DNS name. |
| Endpoint.OS | String | Asset operating system. |
| Endpoint.MACAddress | String | Asset MAC address. |
| Endpoint.IPAddress | Unknown | IP addresses of the endpoint. |
| QRadar.Asset.Interfaces.id | Number | ID of the interface. |
| QRadar.Asset.Interfaces.mac_address | String | MAC address of the interface. Null if unknown. |
| QRadar.Asset.Interfaces.ip_addresses.id | Number | ID of the interface. |
| QRadar.Asset.Interfaces.ip_addresses.network_id | Number | Network ID of the network the IP belongs to. |
| QRadar.Asset.Interfaces.ip_addresses.value | String | The IP address. |
| QRadar.Asset.Interfaces.ip_addresses.type | String | Type of IP address. Possible values: “IPV4”, “IPV6”. |
| QRadar.Asset.Interfaces.ip_addresses.created | Date | Date when the IP address was created. |
| QRadar.Asset.Interfaces.ip_addresses.first_seen_scanner | Date | Date when the IP address was first seen during a vulnerability scan. |
| QRadar.Asset.Interfaces.ip_addresses.first_seen_profiler | Date | Date when the IP address was first seen in event or flow traffic. |
| QRadar.Asset.Interfaces.ip_addresses.last_seen_scanner | Date | Date when the IP address was most recently seen during a vulnerability scan. |
| QRadar.Asset.Interfaces.ip_addresses.last_seen_profiler | Date | Date when the IP address was most recently seen in event or flow traffic. |
| QRadar.Asset.Products.id | Number | The ID of this software product instance in QRadar’s asset model. |
| QRadar.Asset.Products.product_variant_id | Number | The ID of this software product variant in QRadar’s catalog of products. |
| QRadar.Asset.Products.first_seen_scanner | Date | Date when the product was first seen during a vulnerability scan. |
| QRadar.Asset.Products.first_seen_profiler | Date | Date when the product was first seen in event or flow traffic. |
| QRadar.Asset.Products.last_seen_scanner | Date | Date when the product was most recently seen seen during a vulnerability scan. |
| QRadar.Asset.Products.last_seen_profiler | Date | Date when the product was most recently seen in event or flow traffic. |
| QRadar.Asset.VulnerabilityCount | Number | The total number of vulnerabilities associated with this asset. |
| QRadar.Asset.RiskScoreSum | Number | The sum of the CVSS scores of the vulnerabilities on this asset. |
| QRadar.Asset.Hostnames.last_seen_profiler | Date | Date when the host was most recently seen in event or flow traffic. |
| QRadar.Asset.Hostnames.created | Date | Date when the host was created. |
| QRadar.Asset.Hostnames.last_seen_scanner | Date | Date when the host was most recently seen during a vulnerability scan. |
| QRadar.Asset.Hostnames.name | String | Name of the host. |
| QRadar.Asset.Hostnames.first_seen_scanner | Date | Date when the host was first seen during a vulnerability scan. |
| QRadar.Asset.Hostnames.id | Number | ID of the host. |
| QRadar.Asset.Hostnames.type | String | Type of the host. Possible values: “DNS”, “NETBIOS”, “NETBIOSGROUP”. |
| QRadar.Asset.Hostnames.first_seen_profiler | Date | Date when the host was first seen in event or flow traffic. |
| QRadar.Asset.ID | Number | ID of the asset. |
| QRadar.Asset.Users.last_seen_profiler | Date | Date when the user was most recently seen in event or flow traffic. |
| QRadar.Asset.Users.last_seen_scanner | Date | Date when the user was most recently seen during a vulnerability scan. |
| QRadar.Asset.Users.first_seen_scanner | Date | Date when the user was first seen during a vulnerability scan. |
| QRadar.Asset.Users.id | Number | ID of the user. |
| QRadar.Asset.Users.first_seen_profiler | Date | Date when the user was first seen in event or flow traffic. |
| QRadar.Asset.Users.username | String | Name of the user. |
| QRadar.Asset.DomainID | Number | ID of the domain this asset belongs to. |
| QRadar.Asset.Properties.last_reported | Date | Date when the property was last updated. |
| QRadar.Asset.Properties.name | String | Name of the property. |
| QRadar.Asset.Properties.type_id | Number | Type ID of the property. |
| QRadar.Asset.Properties.id | Number | ID of the property. |
| QRadar.Asset.Properties.last_reported_by | String | The source of the most recent update to this property. |
| QRadar.Asset.Properties.value | String | Property value. |
Command example
!qradar-assets-list filter="id<1100" range=0-2
Context Example
{
"Endpoint": [
{
"IPAddress": [
"1.1.1.1"
]
}
],
"QRadar": {
"Asset": {
"DomainID": 0,
"ID": 1003,
"Interfaces": [
{
"created": "2023-07-26T14:32:01.789000+00:00",
"id": 1003,
"ip_addresses": [
{
"created": "2023-07-26T14:32:01.789000+00:00",
"id": 1003,
"network_id": 2,
"type": "IPV4",
"value": "1.1.1.1"
}
]
}
],
"Properties": [
{
"id": 1020,
"last_reported": "2023-07-26T14:32:01.802000+00:00",
"last_reported_by": "USER:admin",
"name": "Unified Name",
"type_id": 1002,
"value": "1.1.1.1"
}
],
"RiskScoreSum": 0,
"VulnerabilityCount": 0
}
}
}
Human Readable Output
Assets List
DomainID ID Interfaces Properties RiskScoreSum VulnerabilityCount 0 1003 {‘created’: ‘2023-07-26T14:32:01.789000+00:00’, ‘ip_addresses’: [{‘created’: ‘2023-07-26T14:32:01.789000+00:00’, ‘network_id’: 2, ‘id’: 1003, ‘type’: ‘IPV4’, ‘value’: ‘1.1.1.1’}], ‘id’: 1003} {‘last_reported’: ‘2023-07-26T14:32:01.802000+00:00’, ‘name’: ‘Unified Name’, ‘type_id’: 1002, ‘id’: 1020, ‘last_reported_by’: ‘USER:admin’, ‘value’: ‘1.1.1.1’} 0.0 0 Endpoints
IPAddress 1.1.1.1
qradar-saved-searches-list
Retrieves a list of Ariel saved searches.
Base Command
qradar-saved-searches-list
Input
| Argument Name | Description | Required |
|---|---|---|
| saved_search_id | The saved search ID for which to retrieve its details. Specify saved_search_id to get details about a specific saved search. | Optional |
| timeout | Number of seconds until timeout for the specified command. Default is 35. | Optional |
| range | Range of results to return (e.g.: 0-20, 3-5, 3-3). Default is 0-49. | Optional |
| filter | Query by which to filter saved searches, e.g., “database=EVENTS and is_dashboard=true”. For reference, see: https://www.ibm.com/support/knowledgecenter/SS42VS_SHR/com.ibm.qradarapi.doc/c_rest_api_filtering.html. | Optional |
| fields | Comma-separated list of fields to retrieve in the response. Fields that are not explicitly named are excluded. E.g., “id,owner,description”. Specify subfields in brackets and multiple fields in the same object separated by commas. For a full list of available fields, see: https://ibmsecuritydocs.github.io/qradar_api_14.0/14.0–ariel-saved_searches-GET.html. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.SavedSearch.Owner | String | Owner of the saved search. |
| QRadar.SavedSearch.Description | String | Description of the saved search. |
| QRadar.SavedSearch.CreationDate | Date | Date when saved search was created. |
| QRadar.SavedSearch.UID | String | UID of the saved search. |
| QRadar.SavedSearch.Database | String | The database of the Ariel saved search, events, or flows. |
| QRadar.SavedSearch.QuickSearch | Boolean | Whether the saved search is a quick search. |
| QRadar.SavedSearch.Name | String | Name of the saved search. |
| QRadar.SavedSearch.ModifiedDate | Date | Date when the saved search was most recently modified. |
| QRadar.SavedSearch.ID | Number | ID of the saved search. |
| QRadar.SavedSearch.AQL | String | The AQL query. |
| QRadar.SavedSearch.IsShared | Boolean | Whether the saved search is shared with other users. |
Command example
!qradar-saved-searches-list range=0-1
Context Example
{
"QRadar": {
"SavedSearch": [
{
"AQL": "select QIDNAME(qid) as 'Event Name',logsourcename(logSourceId) as 'Log Source',\"eventCount\" as 'Event Count',\"startTime\" as 'Time',categoryname(category) as 'Low Level Category',\"sourceIP\" as 'Source IP',\"sourcePort\" as 'Source Port',\"destinationIP\" as 'Destination IP',\"destinationPort\" as 'Destination Port',\"userName\" as 'Username',\"magnitude\" as 'Magnitude' from events where \"Experience Center\" ilike '%AWSCloud%' order by \"startTime\" desc LIMIT 1000 start '2023-08-02 08:34' stop '2023-08-02 08:39'",
"CreationDate": "2019-04-02T17:39:08.493000+00:00",
"Database": "EVENTS",
"Description": "",
"ID": 2817,
"IsShared": false,
"ModifiedDate": "2023-02-23T14:12:52.611000+00:00",
"Name": "EC: AWS Cloud Attack Events",
"Owner": "admin",
"QuickSearch": false,
"UID": "0144c7d8-a3fe-47c1-b16b-12721a34077e"
},
{
"AQL": "select * from flows where destinationport = '445' and (FORMAT::PAYLOAD_TO_HEX(sourcepayload) like '%70 00 73 00 65 00 78 00 65 00 63 00 73 00 76 00 63 00%' OR FORMAT::PAYLOAD_TO_HEX(sourcepayload) like '%50 00 53 00 45 00 58 00 45 00 53 00 56 00 43 00 2e 00 45 00 58 00 45%' OR FORMAT::PAYLOAD_TO_HEX(sourcepayload) like '%73 00 76 00 63 00 63 00 74 00 6c 00%' OR FORMAT::PAYLOAD_TO_HEX(sourcepayload) like '%5c 00 61 00 64 00 6d 00 69 00 6e 00 24 00%' OR FORMAT::PAYLOAD_TO_HEX(sourcepayload) like '%ff 53 4d 42 72 00 00 00 00 18 07 c0%') last 24 HOURS",
"CreationDate": "2017-07-02T18:11:44.984000+00:00",
"Database": "FLOWS",
"Description": "",
"ID": 2835,
"IsShared": true,
"ModifiedDate": "2023-03-05T13:34:00.352000+00:00",
"Name": "Potential Ransomware (Suspicious activity, Possible Petya, NotPetya)",
"Owner": "admin",
"QuickSearch": false,
"UID": "0791701a-80e3-4a1c-b11f-7bc943b96bf6"
}
]
}
}
Human Readable Output
Saved Searches List
ID Name IsShared AQL UID QuickSearch ModifiedDate CreationDate Database Owner 2817 EC: AWS Cloud Attack Events false select QIDNAME(qid) as ‘Event Name’,logsourcename(logSourceId) as ‘Log Source’,”eventCount” as ‘Event Count’,”startTime” as ‘Time’,categoryname(category) as ‘Low Level Category’,”sourceIP” as ‘Source IP’,”sourcePort” as ‘Source Port’,”destinationIP” as ‘Destination IP’,”destinationPort” as ‘Destination Port’,”userName” as ‘Username’,”magnitude” as ‘Magnitude’ from events where “Experience Center” ilike ‘%AWSCloud%’ order by “startTime” desc LIMIT 1000 start ‘2023-08-02 08:34’ stop ‘2023-08-02 08:39’ 0144c7d8-a3fe-47c1-b16b-12721a34077e false 2023-02-23T14:12:52.611000+00:00 2019-04-02T17:39:08.493000+00:00 EVENTS admin 2835 Potential Ransomware (Suspicious activity, Possible Petya, NotPetya) true select * from flows where destinationport = ‘445’ and (FORMAT::PAYLOAD_TO_HEX(sourcepayload) like ‘%70 00 73 00 65 00 78 00 65 00 63 00 73 00 76 00 63 00%’ OR FORMAT::PAYLOAD_TO_HEX(sourcepayload) like ‘%50 00 53 00 45 00 58 00 45 00 53 00 56 00 43 00 2e 00 45 00 58 00 45%’ OR FORMAT::PAYLOAD_TO_HEX(sourcepayload) like ‘%73 00 76 00 63 00 63 00 74 00 6c 00%’ OR FORMAT::PAYLOAD_TO_HEX(sourcepayload) like ‘%5c 00 61 00 64 00 6d 00 69 00 6e 00 24 00%’ OR FORMAT::PAYLOAD_TO_HEX(sourcepayload) like ‘%ff 53 4d 42 72 00 00 00 00 18 07 c0%’) last 24 HOURS 0791701a-80e3-4a1c-b11f-7bc943b96bf6 false 2023-03-05T13:34:00.352000+00:00 2017-07-02T18:11:44.984000+00:00 FLOWS admin
qradar-searches-list
Retrieves the list of Ariel searches IDs. Search status and results can be polled by sending the search ID to the ‘qradar-search-status-get’ and ‘qradar-search-results-get’ commands.
Base Command
qradar-searches-list
Input
| Argument Name | Description | Required |
|---|---|---|
| range | Range of results to return (e.g.: 0-20, 3-5, 3-3). Default is 0-49. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.SearchID.SearchID | String | ID of the search. |
Command example
#### Context Example
```json
{
"QRadar": {
"SearchID": [
{
"SearchID": "111e7107-48da-4645-8c2c-a8285d113eac"
},
{
"SearchID": "4ad8f58f-d63b-4555-9d5e-62529e6ac089"
},
{
"SearchID": "909075e0-b450-400c-b641-dd04e46b65bf"
},
{
"SearchID": "f6387ac3-342a-41e7-bcd4-14fe5525dc5c"
},
{
"SearchID": "e15caa93-a01f-49f4-b6be-5c666b0e08c7"
},
{
"SearchID": "36ad7331-149d-4419-947d-ff0d3dd23cf1"
},
{
"SearchID": "a893fc4f-f405-4cb2-a6c3-698dfad6045d"
},
{
"SearchID": "47f40fcd-b7fd-48d7-abbc-05fb447acee2"
},
{
"SearchID": "8967788b-f746-4e5b-9174-c145196eddb1"
},
{
"SearchID": "1d743e24-a524-417f-9747-c967e0328b48"
},
{
"SearchID": "be14bcb5-363e-4547-9b7f-923578a16ae6"
},
{
"SearchID": "a4f4d846-9057-41ae-b558-0dd47134a72a"
},
{
"SearchID": "2c98e5f6-4988-4fe3-bbac-43acbbfaaea2"
},
{
"SearchID": "7e4bd0cf-b04e-446f-ba94-16e1811740e9"
},
{
"SearchID": "76e9ed8a-4f59-42ad-8135-9b1781568935"
},
{
"SearchID": "04e953e2-153a-488b-9a53-8701d16431c4"
},
{
"SearchID": "cd44255c-0496-4663-a6ac-9662ad4a13ef"
},
{
"SearchID": "92e5d6ec-1c60-4c7b-ad97-7610c1e7ed90"
},
{
"SearchID": "8ded0056-8853-443f-9f99-3fff30c08cd6"
},
{
"SearchID": "5ced0d93-4237-461a-ba12-6513d5674fb0"
},
{
"SearchID": "1de985b2-2d45-4d0e-ac86-d25c5b7d8803"
},
{
"SearchID": "b19000dd-1eda-4b85-a45e-334478f0755f"
},
{
"SearchID": "ae52c4d7-689f-4274-8f74-6de74bd3652c"
},
{
"SearchID": "70fe39d6-4e8e-4c48-8207-12d5930544f4"
},
{
"SearchID": "011e1de5-c985-462a-8252-e291acaed012"
},
{
"SearchID": "174d5a7c-b004-4b9c-96fb-868c043daa3c"
},
{
"SearchID": "12b576a1-410e-4c46-bd95-61b2bebb4ceb"
},
{
"SearchID": "8f1b645a-6f81-43fc-86f3-23c2213359b6"
},
{
"SearchID": "12463d5a-4d2c-4c5b-9640-88e4cdee245c"
},
{
"SearchID": "e1d2697f-4c40-46d0-b8b3-90a51e732814"
},
{
"SearchID": "e9dea979-039c-409a-8a60-fe1fe44fa3c2"
},
{
"SearchID": "66ca4a44-a3ac-482b-9d7a-300d621eb8a9"
},
{
"SearchID": "a42ef950-fd27-42c9-af3d-8d466bf73d5d"
},
{
"SearchID": "73ee61f0-c480-4145-a00b-d8c3a55de791"
},
{
"SearchID": "e73b8002-b47d-4fb3-9dae-34a99dd21943"
},
{
"SearchID": "5b812a3b-624d-4cf7-a2ed-f2d1f469b0a4"
},
{
"SearchID": "4f6e37db-8c4f-4e2f-9b27-b8ae68d0c38b"
},
{
"SearchID": "df9cf783-d706-46ed-8be7-680a0830d3eb"
},
{
"SearchID": "d2aa7f7c-dbf4-405f-9652-b6bb776164f0"
},
{
"SearchID": "de72022b-8070-4151-a826-16eb913db2cd"
},
{
"SearchID": "32501bb7-22b8-4d79-aa7b-0b565c4bd806"
},
{
"SearchID": "f89f3515-6d27-4616-a1bb-7dc008aa1562"
},
{
"SearchID": "1ba37caf-c969-43fa-b037-6c164535b512"
},
{
"SearchID": "78b709c4-8037-43dc-8bf0-dfd94629674f"
},
{
"SearchID": "88096e2b-4feb-4071-807e-96e3dfc080ff"
},
{
"SearchID": "1a7576fc-2cc1-41c6-85b6-3728d4d44f3d"
},
{
"SearchID": "b4414e3b-b5ec-4cc8-9db2-35a7a2057e46"
},
{
"SearchID": "c6a82de6-cfef-444e-9b0f-b124b5599b7b"
},
{
"SearchID": "185971cc-ebbb-453d-b826-bffc59836be1"
},
{
"SearchID": "2a45ec38-d060-4aae-9a9c-730f49966fdc"
}
]
}
}
Human Readable Output
Search ID List
SearchID 111e7107-48da-4645-8c2c-a8285d113eac 4ad8f58f-d63b-4555-9d5e-62529e6ac089 909075e0-b450-400c-b641-dd04e46b65bf f6387ac3-342a-41e7-bcd4-14fe5525dc5c e15caa93-a01f-49f4-b6be-5c666b0e08c7 36ad7331-149d-4419-947d-ff0d3dd23cf1 a893fc4f-f405-4cb2-a6c3-698dfad6045d 47f40fcd-b7fd-48d7-abbc-05fb447acee2 8967788b-f746-4e5b-9174-c145196eddb1 1d743e24-a524-417f-9747-c967e0328b48 be14bcb5-363e-4547-9b7f-923578a16ae6 a4f4d846-9057-41ae-b558-0dd47134a72a 2c98e5f6-4988-4fe3-bbac-43acbbfaaea2 7e4bd0cf-b04e-446f-ba94-16e1811740e9 76e9ed8a-4f59-42ad-8135-9b1781568935 04e953e2-153a-488b-9a53-8701d16431c4 cd44255c-0496-4663-a6ac-9662ad4a13ef 92e5d6ec-1c60-4c7b-ad97-7610c1e7ed90 8ded0056-8853-443f-9f99-3fff30c08cd6 5ced0d93-4237-461a-ba12-6513d5674fb0 1de985b2-2d45-4d0e-ac86-d25c5b7d8803 b19000dd-1eda-4b85-a45e-334478f0755f ae52c4d7-689f-4274-8f74-6de74bd3652c 70fe39d6-4e8e-4c48-8207-12d5930544f4 011e1de5-c985-462a-8252-e291acaed012 174d5a7c-b004-4b9c-96fb-868c043daa3c 12b576a1-410e-4c46-bd95-61b2bebb4ceb 8f1b645a-6f81-43fc-86f3-23c2213359b6 12463d5a-4d2c-4c5b-9640-88e4cdee245c e1d2697f-4c40-46d0-b8b3-90a51e732814 e9dea979-039c-409a-8a60-fe1fe44fa3c2 66ca4a44-a3ac-482b-9d7a-300d621eb8a9 a42ef950-fd27-42c9-af3d-8d466bf73d5d 73ee61f0-c480-4145-a00b-d8c3a55de791 e73b8002-b47d-4fb3-9dae-34a99dd21943 5b812a3b-624d-4cf7-a2ed-f2d1f469b0a4 4f6e37db-8c4f-4e2f-9b27-b8ae68d0c38b df9cf783-d706-46ed-8be7-680a0830d3eb d2aa7f7c-dbf4-405f-9652-b6bb776164f0 de72022b-8070-4151-a826-16eb913db2cd 32501bb7-22b8-4d79-aa7b-0b565c4bd806 f89f3515-6d27-4616-a1bb-7dc008aa1562 1ba37caf-c969-43fa-b037-6c164535b512 78b709c4-8037-43dc-8bf0-dfd94629674f 88096e2b-4feb-4071-807e-96e3dfc080ff 1a7576fc-2cc1-41c6-85b6-3728d4d44f3d b4414e3b-b5ec-4cc8-9db2-35a7a2057e46 c6a82de6-cfef-444e-9b0f-b124b5599b7b 185971cc-ebbb-453d-b826-bffc59836be1 2a45ec38-d060-4aae-9a9c-730f49966fdc
qradar-search-create
Creates a new asynchronous Ariel search. Returns the search ID. Search status and results can be polled by sending the search ID to the ‘qradar-search-status-get’ and ‘qradar-search-results-get’ commands. Accepts SELECT query expressions only.
Base Command
qradar-search-create
Input
| Argument Name | Description | Required |
|---|---|---|
| offense_id | The ID of the offense to retrieve. Mutually exclusive with query_expression and saved_search_id. | Optional |
| events_limit | The number of events to return. Mutually exclusive with query_expression and saved_search_id. | Optional |
| events_columns | Comma separated list of columns to return. Mutually exclusive with query_expression and saved_search_id. | Optional |
| fetch_mode | The mode to use when fetching events. Mutually exclusive with query_expression and saved_search_id. Possible values are: Fetch With All Events, Fetch Correlation Events Only. | Optional |
| start_time | The start time of the search. | Optional |
| query_expression | The AQL query to execute. Mutually exclusive with all other arguments. | Optional |
| saved_search_id | Saved search ID to execute. Mutually exclusive with all other arguments. Saved search ID is the ‘id’ field returned by the ‘qradar-saved-searches-list’ command. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.Search.Status | String | Status of the newly created search. |
| QRadar.Search.ID | String | ID of the newly created search. |
Command example
!qradar-search-create query_expression="""SELECT "destinationPort" AS 'Destination Port', UniqueCount("sourceIP") AS 'Source IP (Unique Count)', UniqueCount("destinationIP") AS 'Destination IP (Unique Count)', UniqueCount(qid) AS 'Event Name (Unique Count)', UniqueCount(logSourceId) AS 'Log Source (Unique Count)', UniqueCount(category) AS 'Low Level Category (Unique Count)', UniqueCount("protocolId") AS 'Protocol (Unique Count)', UniqueCount("userName") AS 'Username (Unique Count)', MAX("magnitude") AS 'Magnitude (Maximum)', SUM("eventCount") AS 'Event Count (Sum)', COUNT(*) AS 'Count' from events where ( ("creEventList"='100120') or ("creEventList"='100122') or ("creEventList"='100135') AND "eventDirection"='R2L' ) GROUP BY "destinationPort" order by "Event Count (Sum)" desc last 6 hours"""
Context Example
{
"QRadar": {
"Search": {
"ID": "68d4e4e6-f512-4171-b130-d671334cb47d",
"Status": "WAIT"
}
}
}
Human Readable Output
Create Search
ID Status 68d4e4e6-f512-4171-b130-d671334cb47d WAIT
qradar-search-status-get
Retrieves status information for a search, based on the search ID.
Base Command
qradar-search-status-get
Input
| Argument Name | Description | Required |
|---|---|---|
| search_id | The identifier for an Ariel search. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.Search.Status | String | Status of the search. |
| QRadar.Search.ID | String | ID of the search. |
qradar-search-results-get
Retrieves search results.
Base Command
qradar-search-results-get
Input
| Argument Name | Description | Required |
|---|---|---|
| search_id | The identifier for an Ariel search. | Required |
| output_path | Replaces the default context output path for the query result (QRadar.Search.Result). E.g., for output_path=QRadar.Correlations, the result will be under the ‘QRadar.Correlations’ key in the context data. | Optional |
| range | Range of events to return. (e.g.: 0-20, 3-5, 3-3). Default is 0-49. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.Search.Result | Unknown | The result of the search. |
qradar-search-delete
Deleted search from Qradar, based on the search ID.
Base Command
qradar-search-delete
Input
| Argument Name | Description | Required |
|---|---|---|
| search_id | The identifier for an Ariel search. | Required |
qradar-reference-sets-list
qradar-search-cancel
Cancelled search in QRadar based on search_id.
Base Command
qradar-search-cancel
Input
| Argument Name | Description | Required |
|---|---|---|
| search_id | The identifier for an Ariel search. | Required |
qradar-reference-sets-list
Retrieves a list of reference sets.
Base Command
qradar-reference-sets-list
Input
| Argument Name | Description | Required |
|---|---|---|
| ref_name | The reference name of the reference set for which to retrieve its details. Specify ref_name to get details about a specific reference set. | Optional |
| date_value | If set to true will try to convert the data values to ISO-8601 string. Possible values are: True, False. Default is False. | Optional |
| range | Range of results to return (e.g.: 0-20, 3-5, 3-3). Default is 0-49. | Optional |
| filter | Query by which to filter reference sets, e.g., “timeout_type=FIRST_SEEN”. For reference, see: https://www.ibm.com/support/knowledgecenter/SS42VS_SHR/com.ibm.qradarapi.doc/c_rest_api_filtering.html. | Optional |
| fields | Comma-separated list of fields to retrieve in the response. Fields that are not explicitly named are excluded. E.g., “name,timeout_type”. Specify subfields in brackets and multiple fields in the same object separated by commas. For a full list of available fields, see: https://ibmsecuritydocs.github.io/qradar_api_14.0/14.0–reference_data-sets-GET.html. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.Reference.TimeoutType | String | Timeout type of the reference set. Possible values: “UNKNOWN”, “FIRST_SEEN”, “LAST_SEEN”. |
| QRadar.Reference.NumberOfElements | Number | Number of elements in the reference set. |
| QRadar.Reference.TimeToLive | String | Time left to live for the reference. |
| QRadar.Reference.Data.LastSeen | Date | Date when this data was last seen. |
| QRadar.Reference.Data.FirstSeen | Date | Date when this data was first seen. |
| QRadar.Reference.Data.Source | String | Source of this data. |
| QRadar.Reference.Data.Value | String | Data value. |
| QRadar.Reference.CreationTime | Date | Date when the reference set was created. |
| QRadar.Reference.Name | String | Name of the reference set. |
| QRadar.Reference.ElementType | String | Type of the elements in the reference set. |
Command example
!qradar-reference-sets-list filter="timeout_type=FIRST_SEEN" range=0-2
Context Example
{
"QRadar": {
"Reference": [
{
"CreationTime": "2015-08-27T17:15:40.583000+00:00",
"ElementType": "IP",
"Name": "Critical Assets",
"NumberOfElements": 0,
"TimeoutType": "FIRST_SEEN"
},
{
"CreationTime": "2017-10-25T16:31:15.992000+00:00",
"ElementType": "ALNIC",
"Name": "BadRabbit_FileHash",
"NumberOfElements": 6,
"TimeoutType": "FIRST_SEEN"
},
{
"CreationTime": "2022-10-03T10:38:51.140000+00:00",
"ElementType": "IP",
"Name": "Windows RCE IPs",
"NumberOfElements": 19,
"TimeoutType": "FIRST_SEEN"
}
]
}
}
Human Readable Output
Reference Sets List
Name ElementType TimeoutType CreationTime NumberOfElements Critical Assets IP FIRST_SEEN 2015-08-27T17:15:40.583000+00:00 0 BadRabbit_FileHash ALNIC FIRST_SEEN 2017-10-25T16:31:15.992000+00:00 6 Windows RCE IPs IP FIRST_SEEN 2022-10-03T10:38:51.140000+00:00 19
qradar-reference-set-create
Creates a new reference set.
Base Command
qradar-reference-set-create
Input
| Argument Name | Description | Required |
|---|---|---|
| ref_name | The name of the reference set to be created. Reference names can be found by ‘Name’ field in ‘qradar-reference-sets-list’ command. | Required |
| element_type | The element type for the values allowed in the reference set. Possible values are: ALN, ALNIC, NUM, IP, PORT, DATE. | Required |
| timeout_type | Indicates if the time_to_live interval is based on when the data was first seen or last seen. Possible values are: FIRST_SEEN, LAST_SEEN, UNKNOWN. Default is UNKNOWN. | Optional |
| time_to_live | The time to live interval, time range. for example: ‘1 month’ or ‘5 minutes’. | Optional |
| fields | Comma-separated list of fields to retrieve in the response. Fields that are not explicitly named are excluded. E.g., “name,timeout_type”. Specify subfields in brackets and multiple fields in the same object separated by commas. For a full list of available fields, see: https://ibmsecuritydocs.github.io/qradar_api_14.0/14.0–reference_data-sets-POST.html. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.Reference.TimeoutType | String | Timeout type of the reference set. Possible values: “UNKNOWN”, “FIRST_SEEN”, “LAST_SEEN”. |
| QRadar.Reference.NumberOfElements | Number | Number of elements in the reference set. |
| QRadar.Reference.TimeToLive | String | Time left to live for the reference. |
| QRadar.Reference.Data.LastSeen | Date | Date when this data was last seen. |
| QRadar.Reference.Data.FirstSeen | Date | Date when this data was first seen. |
| QRadar.Reference.Data.Source | String | Source of this data. |
| QRadar.Reference.Data.Value | String | Data value. |
| QRadar.Reference.CreationTime | Date | Date when the reference set was created. |
| QRadar.Reference.Name | String | Name of the reference set. |
| QRadar.Reference.ElementType | String | Type of the elements in the reference set. |
Command example
!qradar-reference-set-create element_type=IP ref_name="Malicious IPs" time_to_live="1 year" timeout_type=FIRST_SEEN
Context Example
{
"QRadar": {
"Reference": {
"CreationTime": "2023-08-02T08:39:30.887000+00:00",
"ElementType": "IP",
"Name": "Malicious IPs",
"NumberOfElements": 0,
"TimeToLive": "1 years 0 mons 0 days 0 hours 0 mins 0.0 secs",
"TimeoutType": "FIRST_SEEN"
}
}
}
Human Readable Output
Reference Set Create
Name ElementType TimeToLive TimeoutType CreationTime NumberOfElements Malicious IPs IP 1 years 0 mons 0 days 0 hours 0 mins 0.0 secs FIRST_SEEN 2023-08-02T08:39:30.887000+00:00 0
qradar-reference-set-delete
Removes a reference set or purges its contents.
Base Command
qradar-reference-set-delete
Input
| Argument Name | Description | Required |
|---|---|---|
| ref_name | The name of the reference set to be deleted. Reference names can be found by ‘Name’ field in ‘qradar-reference-sets-list’ command. | Required |
| purge_only | Indicates if the reference set should have its contents purged (true), keeping the reference set structure. If the value is ‘false’, or not specified the reference set is removed completely. Possible values are: true, false. Default is false. | Optional |
Context Output
There is no context output for this command.
Command example
!qradar-reference-set-delete ref_name="Malicious IPs"
Human Readable Output
Request to delete reference Malicious IPs was submitted. Current deletion status: QUEUED
qradar-reference-set-value-upsert
Adds or updates an element in a reference set.
Base Command
qradar-reference-set-value-upsert
Input
| Argument Name | Description | Required |
|---|---|---|
| ref_name | The name of the reference set to add or update an element in. Reference names can be found by the ‘Name’ field in the ‘qradar-reference-sets-list’ command. | Required |
| value | Comma-separated list of the values to add or update in the reference set. If the values are dates, the supported date formats are: epoch, ISO, and time range (<number> <time unit>’, e.g., 12 hours, 7 days.). | Required |
| source | An indication of where the data originated. Default is reference data api. | Optional |
| date_value | True if the specified value type was date. Possible values are: true, false. | Optional |
| fields | Comma-separated list of fields to retrieve in the response. Fields that are not explicitly named are excluded. E.g., “name,timeout_type”. Specify subfields in brackets and multiple fields in the same object separated by commas. For a full list of available fields, see: https://ibmsecuritydocs.github.io/qradar_api_14.0/14.0–reference_data-sets-name-POST.html. | Optional |
| quiet_mode | If true, does not output the updated reference set data. This argument helps avoid large outputs when the reference set is large. Possible values are: true, false. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.Reference.TimeoutType | String | Timeout type of the reference set. Possible values: “UNKNOWN”, “FIRST_SEEN”, “LAST_SEEN”. |
| QRadar.Reference.NumberOfElements | Number | Number of elements in the reference set. |
| QRadar.Reference.TimeToLive | String | Time left to live for the reference. |
| QRadar.Reference.Data.LastSeen | Date | Date when this data was last seen. |
| QRadar.Reference.Data.FirstSeen | Date | Date when this data was first seen. |
| QRadar.Reference.Data.Source | String | Source of this data. |
| QRadar.Reference.Data.Value | String | Data value. |
| QRadar.Reference.CreationTime | Date | Date when the reference set was created. |
| QRadar.Reference.Name | String | Name of the reference set. |
| QRadar.Reference.ElementType | String | Type of the elements in the reference set. |
Command example
!qradar-reference-set-value-upsert ref_name="Malicious IPs" value="1.1.1.1,1.1.1.1,1.1.1.1"
Context Example
{
"QRadar": {
"Reference": {
"CreationTime": "2023-08-02T08:39:30.887000+00:00",
"Data": [
{
"first_seen": 1690965572017,
"last_seen": 1690965572017,
"source": "reference data api",
"value": "1.1.1.1"
},
{
"first_seen": 1690965572017,
"last_seen": 1690965572017,
"source": "reference data api",
"value": "1.1.1.1"
},
{
"first_seen": 1690965572017,
"last_seen": 1690965572017,
"source": "reference data api",
"value": "1.1.1.1"
}
],
"ElementType": "IP",
"Name": "Malicious IPs",
"NumberOfElements": 3,
"TimeToLive": "1 years 0 mons 0 days 0 hours 0 mins 0.0 secs",
"TimeoutType": "FIRST_SEEN"
}
}
}
Human Readable Output
Reference Update Create
CreationTime Data ElementType Name NumberOfElements TimeToLive TimeoutType 2023-08-02T08:39:30.887000+00:00 {‘last_seen’: 1690965572017, ‘first_seen’: 1690965572017, ‘source’: ‘reference data api’, ‘value’: ‘1.1.1.1’},
{‘last_seen’: 1690965572017, ‘first_seen’: 1690965572017, ‘source’: ‘reference data api’, ‘value’: ‘1.1.1.1’},
{‘last_seen’: 1690965572017, ‘first_seen’: 1690965572017, ‘source’: ‘reference data api’, ‘value’: ‘1.1.1.1’}IP Malicious IPs 3 1 years 0 mons 0 days 0 hours 0 mins 0.0 secs FIRST_SEEN
qradar-reference-set-value-delete
Removes a value from a reference set.
Base Command
qradar-reference-set-value-delete
Input
| Argument Name | Description | Required |
|---|---|---|
| ref_name | The name of the reference set from which to remove a value. Reference names can be found by the ‘Name’ field in the ‘qradar-reference-sets-list’ command. | Required |
| value | The value to remove from the reference set. If the specified value is date, the supported date formats are: epoch, ISO, and time range (<number> <time unit>, e.g., 12 hours, 7 days.). | Required |
| date_value | True if the specified value type was date. Possible values are: True, False. | Optional |
Context Output
There is no context output for this command.
Command example
!qradar-reference-set-value-delete ref_name="Malicious IPs" value="1.1.1.1"
Human Readable Output
value: 1.1.1.1 of reference: Malicious IPs was deleted successfully
qradar-domains-list
Gets the list of domains. You must have System Administrator or Security Administrator permissions to call this endpoint if you are trying to retrieve the details of all domains. You can retrieve details of domains that are assigned to your Security Profile without having the System Administrator or Security Administrator permissions. If you do not have the System Administrator or Security Administrator permissions, then for each domain assigned to your security profile you can only view the values for the ID and name fields. All other values return null.
Base Command
qradar-domains-list
Input
| Argument Name | Description | Required |
|---|---|---|
| domain_id | The domain ID from which to retrieve its details. Specify domain_id to get details about a specific domain. | Optional |
| range | Range of results to return (e.g.: 0-20, 3-5, 3-3). Default is 0-49. | Optional |
| filter | Query by which to filter domains, e.g., “id > 3”. For reference, see: https://www.ibm.com/support/knowledgecenter/SS42VS_SHR/com.ibm.qradarapi.doc/c_rest_api_filtering.html. | Optional |
| fields | Comma-separated list of fields to retrieve in the response. Fields that are not explicitly named are excluded. E.g., “id,name”. Specify subfields in brackets and multiple fields in the same object separated by commas. For a full list of available fields, see: https://ibmsecuritydocs.github.io/qradar_api_14.0/14.0–config-domain_management-domains-GET.html. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.Domains.AssetScannerIDs | Number | Asset scanner IDs that are associated with the domain. |
| QRadar.Domains.CustomProperties | Unknown | Custom properties of the domain. |
| QRadar.Domains.Deleted | Boolean | Whether the domain has been deleted. |
| QRadar.Domains.Description | String | Description of the domain. |
| QRadar.Domains.EventCollectorIDs | Number | Event collector IDs that are assigned to this domain. |
| QRadar.Domains.FlowCollectorIDs | Number | Flow collector IDs that are assigned to this domain. |
| QRadar.Domains.FlowSourceIDs | Number | Flow source IDs that are assigned to this domain. |
| QRadar.Domains.ID | Number | ID of the domain. |
| QRadar.Domains.LogSourceGroupIDs | Number | Log source group IDs that are assigned to this domain. |
| QRadar.Domains.LogSourceIDs | Number | Log source IDs that are assigned to this domain. |
| QRadar.Domains.Name | String | Name of the domain. |
| QRadar.Domains.QVMScannerIDs | Number | QVM scanner IDs that are assigned to this domain. |
| QRadar.Domains.TenantID | Number | ID of the tenant that this domain belongs to. |
Command example
#### Context Example
```json
{
"QRadar": {
"Domains": {
"Deleted": false,
"Description": "",
"ID": 0,
"Name": "",
"TenantID": 0
}
}
}
Human Readable Output
Domains List
Deleted ID TenantID false 0 0
qradar-indicators-upload
Uploads indicators to QRadar.
Base Command
qradar-indicators-upload
Input
| Argument Name | Description | Required |
|---|---|---|
| ref_name | The name of set to add or update data in. Reference names can be found by the ‘Name’ field in the ‘qradar-reference-sets-list’ command. | Required |
| query | The query for getting indicators from Cortex XSOAR. | Optional |
| limit | The maximum number of indicators to fetch from Cortex XSOAR. Default is 50. | Optional |
| page | The page from which to get the indicators. | Optional |
| fields | Comma-separated list of fields to retrieve in the response. Fields that are not explicitly named are excluded. E.g., “name,timeout_type”. Specify subfields in brackets and multiple fields in the same object separated by commas. For a full list of available fields, see: https://ibmsecuritydocs.github.io/qradar_api_14.0/14.0–reference_data-maps-bulk_load-namespace-name-domain_id-POST.html. | Optional |
| quiet_mode | If true, does not output the updated reference set data. This argument helps avoid large outputs when the reference set is large. Possible values are: true, false. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.Reference.TimeoutType | String | Timeout type of the reference set. Possible values: “UNKNOWN”, “FIRST_SEEN”, “LAST_SEEN”. |
| QRadar.Reference.NumberOfElements | Number | Number of elements in the reference set. |
| QRadar.Reference.TimeToLive | String | Time left to live for the reference. |
| QRadar.Reference.Data.LastSeen | Date | Date when this data was last seen. |
| QRadar.Reference.Data.FirstSeen | Date | Date when this data was first seen. |
| QRadar.Reference.Data.Source | String | Source of this data. |
| QRadar.Reference.Data.Value | String | Data value. |
| QRadar.Reference.CreationTime | Date | Date when the reference set was created. |
| QRadar.Reference.Name | String | Name of the reference set. |
| QRadar.Reference.ElementType | String | Type of the elements in the reference set. |
qradar-geolocations-for-ip
Retrieves the MaxMind GeoIP data for the specified IP address.
Base Command
qradar-geolocations-for-ip
Input
| Argument Name | Description | Required |
|---|---|---|
| ip | Comma-separated list of IPs fro which to retrieve their geolocation. | Required |
| fields | Comma-separated list of fields to retrieve in the response. Fields that are not explicitly named are excluded. E.g., “continent,ip_address”. Specify subfields in brackets and multiple fields in the same object separated by commas. For a full list of available fields, see: https://ibmsecuritydocs.github.io/qradar_api_14.0/14.0–services-geolocations-GET.html. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.GeoForIP.CityName | String | Name of the city that is associated with the IP address. |
| QRadar.GeoForIP.ContinentName | String | Name of the continent that is associated with the IP address. |
| QRadar.GeoForIP.LocationAccuracyRadius | Number | The approximate accuracy radius in kilometers around the latitude and longitude for the IP address. |
| QRadar.GeoForIP.LocationAverageIncome | Number | The average income associated with the IP address. |
| QRadar.GeoForIP.LocationLatitude | Number | The approximate latitude of the location associated with the IP address. |
| QRadar.GeoForIP.LocationTimezone | String | Timezone of the location. |
| QRadar.GeoForIP.LocationLongitude | Number | The approximate longitude of the location associated with the IP address. |
| QRadar.GeoForIP.LocationMetroCode | Number | The metro code associated with the IP address. These are only available for IP addresses in the US. Returns the same metro codes as the Google AdWords API. |
| QRadar.GeoForIP.LocationPopulationDensity | Number | The estimated number of people per square kilometer. |
| QRadar.GeoForIP.PhysicalCountryIsoCode | String | ISO code of country where MaxMind believes the end user is located. |
| QRadar.GeoForIP.PhysicalCountryName | String | Name of country where MaxMind believes the end user is located. |
| QRadar.GeoForIP.RegisteredCountryIsoCode | String | ISO code of the country that the ISP has registered the IP address. |
| QRadar.GeoForIP.RegisteredCountryName | String | Name of the country that the ISP has registered the IP address. |
| QRadar.GeoForIP.RepresentedCountryIsoCode | String | ISO code of the country that is represented by users of the IP address. |
| QRadar.GeoForIP.RepresentedCountryName | String | Name of the country that is represented by users of the IP address. |
| QRadar.GeoForIP.RepresentedCountryConfidence | Number | Value between 0-100 that represents MaxMind’s confidence that the represented country is correct. |
| QRadar.GeoForIP.IPAddress | String | IP address to look up. |
| QRadar.GeoForIP.Traits.autonomous_system_number | Number | The autonomous system number associated with the IP address. |
| QRadar.GeoForIP.Traits.autonomous_system_organization | String | The organization associated with the registered autonomous system number for the IP address. |
| QRadar.GeoForIP.Traits.domain | String | The second level domain associated with the IP address. |
| QRadar.GeoForIP.Traits.internet_service_provider | String | The name of the internet service provider associated with the IP address. |
| QRadar.GeoForIP.Traits.organization | String | The name of the organization associated with the IP address. |
| QRadar.GeoForIP.Traits.user_type | String | The user type associated with the IP address. |
| QRadar.GeoForIP.Coordinates | Number | Latitude and longitude by MaxMind. |
| QRadar.GeoForIP.PostalCode | String | The postal code associated with the IP address. |
| QRadar.GeoForIP.PostalCodeConfidence | Number | Value between 0-100 that represents MaxMind’s confidence that the postal code is correct. |
Command example
!qradar-geolocations-for-ip ip="1.1.1.1,1.1.1.1" range=0-1
Context Example
{
"QRadar": {
"GeoForIP": [
{
"CityName": "Mukilteo",
"ContinentName": "NorthAmerica",
"Coordinates": [
47.913,
-122.3042
],
"IPAddress": "1.1.1.1",
"LocationAccuracyRadius": 1000,
"LocationLatitude": 47.913,
"LocationLongitude": -122.3042,
"LocationMetroCode": 819,
"LocationTimezone": "America/Los_Angeles",
"PhysicalCountryIsoCode": "US",
"PhysicalCountryName": "United States",
"PostalCode": "98275",
"RegisteredCountryIsoCode": "US",
"RegisteredCountryName": "United States"
},
{
"CityName": "Mukilteo",
"ContinentName": "NorthAmerica",
"Coordinates": [
47.913,
-122.3042
],
"IPAddress": "1.1.1.1",
"LocationAccuracyRadius": 1000,
"LocationLatitude": 47.913,
"LocationLongitude": -122.3042,
"LocationMetroCode": 819,
"LocationTimezone": "America/Los_Angeles",
"PhysicalCountryIsoCode": "US",
"PhysicalCountryName": "United States",
"PostalCode": "98275",
"RegisteredCountryIsoCode": "US",
"RegisteredCountryName": "United States"
}
]
}
}
Human Readable Output
Geolocation For IP
CityName ContinentName Coordinates IPAddress LocationAccuracyRadius LocationLatitude LocationLongitude LocationMetroCode LocationTimezone PhysicalCountryIsoCode PhysicalCountryName PostalCode RegisteredCountryIsoCode RegisteredCountryName Mukilteo NorthAmerica 47.913,
-122.30421.1.1.1 1000 47.913 -122.3042 819 America/Los_Angeles US United States 98275 US United States Mukilteo NorthAmerica 47.913,
-122.30421.1.1.1 1000 47.913 -122.3042 819 America/Los_Angeles US United States 98275 US United States
qradar-log-sources-list
Retrieves a list of log sources.
Base Command
qradar-log-sources-list
Input
| Argument Name | Description | Required |
|---|---|---|
| qrd_encryption_algorithm | The algorithm to use for encrypting the sensitive data of this endpoint. Possible values are: AES128, AES256. Default is AES128. | Required |
| qrd_encryption_password | The password to use for encrypting the sensitive data of this endpoint. If password was not given, random password will be generated. | Optional |
| range | Range of results to return (e.g.: 0-20, 3-5, 3-3). Default is 0-49. | Optional |
| filter | Query by which to filter log sources, e.g., “auto_discovered=false”. For reference, see: https://www.ibm.com/support/knowledgecenter/SS42VS_SHR/com.ibm.qradarapi.doc/c_rest_api_filtering.html. | Optional |
| fields | Comma-separated list of fields to retrieve in the response. Fields that are not explicitly named are excluded. E.g., “id,name,status”. Specify subfields in brackets and multiple fields in the same object separated by commas. For a full list of available fields, see: https://ibmsecuritydocs.github.io/qradar_api_14.0/14.0–config-event_sources-log_source_management-log_sources-GET.html. | Optional |
| id | ID of a specific log source. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.LogSource.SendingIP | String | IP of the system which the log source is associated with, or fed by. |
| QRadar.LogSource.Internal | Boolean | Whether log source is internal. |
| QRadar.LogSource.ProtocolParameters | Unknown | Protocol parameters. |
| QRadar.LogSource.Description | String | Description of the log source. |
| QRadar.LogSource.Enabled | Boolean | Whether log source is enabled. |
| QRadar.LogSource.GroupIDs | Number | Log source group IDs. |
| QRadar.LogSource.Credibility | Number | Credibility of the log source. |
| QRadar.LogSource.ID | Number | ID of the log source. |
| QRadar.LogSource.ProtocolTypeID | Number | Protocol type used by log source. |
| QRadar.LogSource.CreationDate | Date | Date when log source was created. |
| QRadar.LogSource.Name | String | Name of the log source. |
| QRadar.LogSource.AutoDiscovered | Boolean | Whether log source was auto discovered. |
| QRadar.LogSource.ModifiedDate | Date | Date when log source was last modified. |
| QRadar.LogSource.TypeID | Number | The log source type. |
| QRadar.LogSource.LastEventTime | Date | Date when the last event was received by the log source. |
| QRadar.LogSource.Gateway | Boolean | Whether log source is configured as a gateway. |
| QRadar.LogSource.Status | Unknown | Status of the log source. |
Command example
!qradar-log-sources-list qrd_encryption_algorithm=AES128 range=0-2
Context Example
{
"QRadar": {
"LogSource": [
{
"AutoDiscovered": false,
"CreationDate": "2022-11-21T18:45:24.624000+00:00",
"Credibility": 10,
"Description": "Search Results",
"Enabled": true,
"Gateway": false,
"GroupIDs": [
0
],
"ID": 68,
"Internal": true,
"LastEventTime": "1970-01-01T00:00:00+00:00",
"ModifiedDate": "2022-11-21T18:45:24.624000+00:00",
"Name": "Search Results-2 :: ip-172-31-17-10",
"ProtocolParameters": [
{
"id": 0,
"name": "identifier",
"value": "1.1.1.1"
},
{
"id": 1,
"name": "incomingPayloadEncoding",
"value": "UTF-8"
}
],
"ProtocolTypeID": 0,
"Status": {
"last_updated": 0,
"status": "NA"
},
"TypeID": 355
},
{
"AutoDiscovered": false,
"CreationDate": "2022-11-21T18:45:24.621000+00:00",
"Credibility": 10,
"Description": "System Notification",
"Enabled": true,
"Gateway": false,
"GroupIDs": [
0
],
"ID": 65,
"Internal": true,
"LastEventTime": "2023-08-02T08:39:00.106000+00:00",
"ModifiedDate": "2022-11-21T18:45:24.621000+00:00",
"Name": "System Notification-2 :: ip-172-31-17-10",
"ProtocolParameters": [
{
"id": 0,
"name": "identifier",
"value": "1.1.1.1"
},
{
"id": 1,
"name": "incomingPayloadEncoding",
"value": "UTF-8"
}
],
"ProtocolTypeID": 0,
"Status": {
"last_updated": 0,
"status": "SUCCESS"
},
"TypeID": 147
},
{
"AutoDiscovered": true,
"CreationDate": "2018-10-24T15:25:21.928000+00:00",
"Credibility": 5,
"Description": "WindowsAuthServer device",
"Enabled": true,
"Gateway": false,
"GroupIDs": [
0
],
"ID": 1262,
"Internal": false,
"LastEventTime": "2023-06-12T08:17:22.292000+00:00",
"ModifiedDate": "2023-02-23T14:12:45.774000+00:00",
"Name": "Experience Center: WindowsAuthServer @ 1.1.1.1",
"ProtocolParameters": [
{
"id": 0,
"name": "identifier",
"value": "1.1.1.1"
},
{
"id": 1,
"name": "incomingPayloadEncoding",
"value": "UTF-8"
}
],
"ProtocolTypeID": 0,
"Status": {
"last_updated": 0,
"messages": [
{
"severity": "ERROR",
"text": "Events have not been received from this Log Source in over 720 minutes."
}
],
"status": "ERROR"
},
"TypeID": 12
}
]
}
}
Human Readable Output
Log Sources List
ID Name Description Internal ProtocolParameters Credibility GroupIDs CreationDate Status Enabled ProtocolTypeID AutoDiscovered Gateway TypeID ModifiedDate LastEventTime 68 Search Results-2 :: ip-172-31-17-10 Search Results true {‘name’: ‘identifier’, ‘id’: 0, ‘value’: ‘1.1.1.1’},
{‘name’: ‘incomingPayloadEncoding’, ‘id’: 1, ‘value’: ‘UTF-8’}10 0 2022-11-21T18:45:24.624000+00:00 last_updated: 0
status: NAtrue 0 false false 355 2022-11-21T18:45:24.624000+00:00 1970-01-01T00:00:00+00:00 65 System Notification-2 :: ip-172-31-17-10 System Notification true {‘name’: ‘identifier’, ‘id’: 0, ‘value’: ‘1.1.1.1’},
{‘name’: ‘incomingPayloadEncoding’, ‘id’: 1, ‘value’: ‘UTF-8’}10 0 2022-11-21T18:45:24.621000+00:00 last_updated: 0
status: SUCCESStrue 0 false false 147 2022-11-21T18:45:24.621000+00:00 2023-08-02T08:39:00.106000+00:00 1262 Experience Center: WindowsAuthServer @ 1.1.1.1 WindowsAuthServer device false {‘name’: ‘identifier’, ‘id’: 0, ‘value’: ‘1.1.1.1’},
{‘name’: ‘incomingPayloadEncoding’, ‘id’: 1, ‘value’: ‘UTF-8’}5 0 2018-10-24T15:25:21.928000+00:00 last_updated: 0
messages: {‘severity’: ‘ERROR’, ‘text’: ‘Events have not been received from this Log Source in over 720 minutes.’}
status: ERRORtrue 0 true false 12 2023-02-23T14:12:45.774000+00:00 2023-06-12T08:17:22.292000+00:00
qradar-get-custom-properties
Retrieves a list of event regex properties.
Base Command
qradar-get-custom-properties
Input
| Argument Name | Description | Required |
|---|---|---|
| field_name | A comma-separated list of names of the exact properties to search for. | Optional |
| limit | The maximum number of regex event properties to fetch. Default is 25. | Optional |
| like_name | A comma-separated list names of a properties to search for. Values are case insensitive. | Optional |
| range | Range of results to return (e.g.: 0-20, 3-5, 3-3). Default is 0-49. | Optional |
| filter | Query by which to filter regex properties, e.g., “auto_discovered=false”. For reference, see: https://www.ibm.com/support/knowledgecenter/SS42VS_SHR/com.ibm.qradarapi.doc/c_rest_api_filtering.html. | Optional |
| fields | Comma-separated list of fields to retrieve in the response. Fields that are not explicitly named are excluded. E.g., “id,gateway”. Specify subfields in brackets and multiple fields in the same object separated by commas. For a full list of available fields, see: https://ibmsecuritydocs.github.io/qradar_api_14.0/14.0–config-event_sources-custom_properties-regex_properties-GET.html. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.Properties.identifier | String | ID of the event regex property. |
| QRadar.Properties.modification_date | Date | Date when the event regex property was last updated. |
| QRadar.Properties.datetime_format | String | Date/time pattern that the event regex property matches. |
| QRadar.Properties.property_type | String | Property type. Possible values: “STRING”, “NUMERIC”, “IP”, “PORT”, “TIME”. |
| QRadar.Properties.name | String | Name of the event regex property. |
| QRadar.Properties.auto_discovered | Boolean | Whether the event regex property was auto discovered. |
| QRadar.Properties.description | String | Description of the event regex property. |
| QRadar.Properties.id | Number | ID of the event regex property. |
| QRadar.Properties.use_for_rule_engine | Boolean | Whether the event regex property is parsed when the event is received. |
| QRadar.Properties.creation_date | Date | Date when the event regex property was created. |
| QRadar.Properties.locale | String | Language tag of what locale the property matches. |
| QRadar.Properties.username | String | The owner of the event regex property. |
Command example
!qradar-get-custom-properties filter="id between 90 and 100" range=1-1231
Context Example
{
"QRadar": {
"Properties": [
{
"auto_discovered": false,
"creation_date": "2012-07-04T17:05:02+00:00",
"datetime_format": "",
"description": "Default custom extraction of Event Summary from DSM payload.",
"id": 97,
"identifier": "DEFAULT_EVENT_SUMMARY",
"locale": "en-US",
"modification_date": "2022-11-21T18:44:07.572000+00:00",
"name": "Event Summary",
"property_type": "string",
"use_for_rule_engine": true,
"username": "admin"
},
{
"auto_discovered": false,
"creation_date": "2009-09-04T16:58:12.961000+00:00",
"datetime_format": "",
"description": "Default custom extraction of Avt-App-VolumePackets from DSM payload.",
"id": 99,
"identifier": "4d616180-00d0-4ba0-b423-bfb54e1b8677",
"locale": "en-US",
"modification_date": "2022-11-21T18:44:08.049000+00:00",
"name": "Packets",
"property_type": "numeric",
"use_for_rule_engine": false,
"username": "admin"
},
{
"auto_discovered": false,
"creation_date": "2010-07-27T13:32:44.494000+00:00",
"datetime_format": "NULL::character varying",
"description": "",
"id": 96,
"identifier": "8eb82a2c-bba7-478f-9248-69fba8baf8c7",
"locale": "NULL::character varying",
"modification_date": "2022-11-21T18:59:14.020000+00:00",
"name": "Parent",
"property_type": "string",
"use_for_rule_engine": true,
"username": "admin"
}
]
}
}
Human Readable Output
Custom Properties
auto_discovered creation_date datetime_format description id identifier locale modification_date name property_type use_for_rule_engine username false 2012-07-04T17:05:02+00:00 Default custom extraction of Event Summary from DSM payload. 97 DEFAULT_EVENT_SUMMARY en-US 2022-11-21T18:44:07.572000+00:00 Event Summary string true admin false 2009-09-04T16:58:12.961000+00:00 Default custom extraction of Avt-App-VolumePackets from DSM payload. 99 4d616180-00d0-4ba0-b423-bfb54e1b8677 en-US 2022-11-21T18:44:08.049000+00:00 Packets numeric false admin false 2010-07-27T13:32:44.494000+00:00 NULL::character varying 96 8eb82a2c-bba7-478f-9248-69fba8baf8c7 NULL::character varying 2022-11-21T18:59:14.020000+00:00 Parent string true admin
qradar-reset-last-run
Resets the fetch incidents last run value, which resets the fetch to its initial fetch state. (Will try to fetch the first available offense).
Base Command
qradar-reset-last-run
Input
There are no input arguments for this command.
Context Output
There is no context output for this command.
qradar-print-context
Returns a redacted snapshot of integration context metrics (queued, in-progress, finished, meta sizes, concurrent active, samples count). This command is intended for debugging purposes and may return sensitive data.
Base Command
qradar-print-context
Input
There are no input arguments for this command.
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.Debug.snapshot.queried_count | number | The number of queried mirrored offenses. |
| QRadar.Debug.snapshot.finished_count | number | The number of finished mirrored offenses. |
| QRadar.Debug.snapshot.fetched_count | number | The number of fetched mirrored offenses. |
| QRadar.Debug.snapshot.in_progress_count | number | The number of currently in progress mirrored offenses. |
| QRadar.Debug.snapshot.waiting_markers_count | number | The number of currently waiting mirrored offenses. |
| QRadar.Debug.snapshot.concurrent_active | number | The number of currently active queries for offenses. |
| QRadar.Debug.snapshot.samples_count | number | The number of sample incidents. |
get-mapping-fields
Returns the list of fields for an incident type. This command should be used for debugging purposes.
Base Command
get-mapping-fields
Input
There are no input arguments for this command.
Context Output
There is no context output for this command.
get-remote-data
Gets remote data from a remote incident. This method does not update the current incident, and should be used for debugging purposes.
Base Command
get-remote-data
Input
| Argument Name | Description | Required |
|---|---|---|
| id | The offense ID. | Required |
| lastUpdate | Date string in local time representing the last time the incident was updated. The incident is only returned if it was modified after the last update time. | Required |
Context Output
There is no context output for this command.
get-modified-remote-data
Returns the list of incidents IDs that were modified since the last update time. Note that this method is for debugging purposes. The get-modified-remote-data command is used as part of the mirroring feature, which is available from version 6.1.
Base Command
get-modified-remote-data
Input
| Argument Name | Description | Required |
|---|---|---|
| lastUpdate | Date string in local time representing the last time the incident was updated. The incident is only returned if it was modified after the last update time. | Optional |
Context Output
There is no context output for this command.
qradar-offenses
Gets offenses from QRadar.
Base Command
qradar-offenses
Input
| Argument Name | Description | Required |
|---|---|---|
| filter | Query by which to filter offenses. For reference, consult: https://www.ibm.com/support/knowledgecenter/en/SS42VS_7.3.1/com.ibm.qradar.doc/c_rest_api_filtering.html. | Optional |
| fields | If used, will filter all fields except for the specified ones. Use this parameter to specify which fields you would like to get back in the response. Fields that are not explicitly named, are excluded. Specify subfields in brackets and multiple fields in the same object separated by commas. The filter uses QRadar’s field names. For reference, consult: https://www.ibm.com/support/knowledgecenter/SSKMKU/com.ibm.qradar.doc_cloud/9.1–siem-offenses-GET.html. | Optional |
| range | Range of results to return. e.g.: 0-20. | Optional |
| headers | Table headers to use the human readable output (if none provided, will show all table headers). | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.Offense.Followup | boolean | Offense followup. |
| QRadar.Offense.Credibility | number | The credibility of the offense. |
| QRadar.Offense.Relevance | number | The relevance of the offense. |
| QRadar.Offense.Severity | number | The severity of the offense. |
| QRadar.Offense.SourceAddress | Unknown | The source addresses that are associated with the offense. |
| QRadar.Offense.DestinationAddress | Unknown | The destination addresses that are associated with the offense. |
| QRadar.Offense.AssignedTo | string | The user the offense is assigned to. |
| QRadar.Offense.StartTime | date | The time (ISO) when the offense was started. |
| QRadar.Offense.ID | int | The ID of the offense. |
| QRadar.Offense.DestinationHostname | Unknown | Destintion hostname. |
| QRadar.Offense.Description | string | The description of the offense. |
| QRadar.Offense.EventCount | number | The number of events that are associated with the offense. |
| QRadar.Offense.OffenseSource | string | The source of the offense. |
| QRadar.Offense.Status | string | The status of the offense. One of “OPEN”, “HIDDEN”, or “CLOSED”. |
| QRadar.Offense.Magnitude | number | The magnitude of the offense. |
| QRadar.Offense.ClosingUser | string | The user that closed the offense. |
| QRadar.Offense.ClosingReason | string | The offense closing reason. |
| QRadar.Offense.CloseTime | date | The time when the offense was closed. |
| QRadar.Offense.LastUpdatedTime | date | The time (ISO) when the offense was last updated. |
| QRadar.Offense.Categories | Unknown | Event categories that are associated with the offense. |
| QRadar.Offense.FlowCount | number | The number of flows that are associated with the offense. |
| QRadar.Offense.FollowUp | boolean | Offense followup. |
| QRadar.Offense.OffenseType | string | A number that represents the offense type. |
| QRadar.Offense.Protected | boolean | Is the offense protected. |
| QRadar.Offense.RemoteDestinationCount | Unknown | The remote destinations that are associated with the offesne. If this value is greater than 0 that means your offense has a remote destination, you will need to use QRadarFullSearch playbook with the following query - SELECT destinationip FROM events WHERE inOffense(<offenseID>) GROUP BY destinationip |
qradar-offense-by-id
Gets offense with matching offense ID from qradar.
Base Command
qradar-offense-by-id
Input
| Argument Name | Description | Required |
|---|---|---|
| offense_id | Offense ID. | Required |
| filter | Query to filter offense. For reference please consult: https://www.ibm.com/support/knowledgecenter/en/SS42VS_7.3.1/com.ibm.qradar.doc/c_rest_api_filtering.html. | Optional |
| fields | If used, will filter all fields except for the specified ones. Use this parameter to specify which fields you would like to get back in the response. Fields that are not explicitly named are excluded. Specify subfields in brackets and multiple fields in the same object separated by commas. The filter uses QRadar’s field names, for reference, consult: https://www.ibm.com/support/knowledgecenter/SSKMKU/com.ibm.qradar.doc_cloud/9.1–siem-offenses-offense_id-GET.html. | Optional |
| headers | Table headers to use in the human readable output (if none provided, will show all table headers). | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.Offense.Followup | boolean | Offense followup. |
| QRadar.Offense.Credibility | number | The credibility of the offense. |
| QRadar.Offense.Relevance | number | The relevance of the offense. |
| QRadar.Offense.Severity | number | The severity of the offense. |
| QRadar.Offense.SourceAddress | Unknown | The source addresses that are associated with the offense. |
| QRadar.Offense.DestinationAddress | Unknown | The local destination addresses that are associated with the offense. If your offense has a remote destination, you will need to use the QRadarFullSearch playbook with the following query - SELECT destinationip FROM events WHERE inOffense(<offenseID>) GROUP BY destinationip. |
| QRadar.Offense.RemoteDestinationCount | Unknown | The remote destination that are associated with the offesne. If this value is greater than 0, it means that your offense has a remote destination, you will need to use the QRadarFullSearch playbook with the following query - SELECT destinationip FROM events WHERE inOffense(<offenseID>) GROUP BY destinationip. |
| QRadar.Offense.AssignedTo | string | The user the offense is assigned to. |
| QRadar.Offense.StartTime | date | The time (ISO) when the offense was started. |
| QRadar.Offense.ID | int | The ID of the offense. |
| QRadar.Offense.DestinationHostname | Unknown | Destintion hostname. |
| QRadar.Offense.Description | string | The description of the offense. |
| QRadar.Offense.EventCount | number | The number of events that are associated with the offense. |
| QRadar.Offense.OffenseSource | string | The source of the offense. |
| QRadar.Offense.Status | string | The status of the offense. One of “OPEN”, “HIDDEN”, or “CLOSED”. |
| QRadar.Offense.Magnitude | number | The magnitude of the offense. |
| QRadar.Offense.ClosingUser | string | The user that closed the offense. |
| QRadar.Offense.ClosingReason | string | The offense closing reason. |
| QRadar.Offense.CloseTime | date | The time when the offense was closed. |
| QRadar.Offense.LastUpdatedTime | date | The time (ISO) when the offense was last updated. |
| QRadar.Offense.Categories | Unknown | Event categories that are associated with the offense. |
| QRadar.Offense.FlowCount | number | The number of flows that are associated with the offense. |
| QRadar.Offense.FollowUp | boolean | Offense followup. |
| QRadar.Offense.OffenseType | string | A number that represents the offense type. |
| QRadar.Offense.Protected | boolean | Is the offense protected. |
| QRadar.Offense.RemoteDestinationCount | Unknown | The remote destinations that are associated with the offesne. If this value is greater than 0 that means your offense has a remote destination, you will need to use QRadarFullSearch playbook with the following query - SELECT destinationip FROM events WHERE inOffense(<offenseID>) GROUP BY destinationip |
qradar-searches
Searches in QRadar using AQL. It is highly recommended to use the playbook ‘QRadarFullSearch’ instead of this command - it will execute the search, and will return the result.
Base Command
qradar-searches
Input
| Argument Name | Description | Required |
|---|---|---|
| query_expression | The query expressions in AQL (for more information about Ariel Query Language, review “https://www.ibm.com/support/knowledgecenter/en/SS42VS_7.3.0/com.ibm.qradar.doc/c_aql_intro.html”). | Required |
| headers | Table headers to use the human readable output (if none provided, will show all table headers). | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.Search.ID | number | Search ID. |
| QRadar.Search.Status | string | The status of the search. |
qradar-get-search
Gets a specific search id and status.
Base Command
qradar-get-search
Input
| Argument Name | Description | Required |
|---|---|---|
| search_id | The search id. | Required |
| headers | Table headers to use the human readable output (if none provided, will show all table headers). | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.Search.ID | number | Search ID. |
| QRadar.Search.Status | string | The status of the search. |
qradar-get-search-results
Gets search results.
Base Command
qradar-get-search-results
Input
| Argument Name | Description | Required |
|---|---|---|
| search_id | The search id. | Required |
| range | Range of results to return. e.g.: 0-20. | Optional |
| headers | Table headers to use the human readable output (if none provided, will show all table headers). | Optional |
| output_path | Replaces the default context output path for the query result (QRadar.Search.Result). e.g. for output_path=QRadar.Correlations the result will be under the key “QRadar.Correlations” in the context data. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.Search.Result | Unknown | The result of the search. |
qradar-get-assets
List all assets found in the model.
Base Command
qradar-get-assets
Input
| Argument Name | Description | Required |
|---|---|---|
| filter | Query to filter assets. For reference please consult: https://www.ibm.com/support/knowledgecenter/en/SS42VS_7.3.1/com.ibm.qradar.doc/c_rest_api_filtering.html. | Optional |
| fields | If used, will filter all fields except for the specified ones. Use this parameter to specify which fields you would like to get back in the response. Fields that are not named are excluded. Specify subfields in brackets and multiple fields in the same object separated by commas. The filter uses QRadar’s field names, for reference, consult: https://www.ibm.com/support/knowledgecenter/SSKMKU/com.ibm.qradar.doc_cloud/9.1–asset_model-assets-GET.html. | Optional |
| range | Range of results to return. e.g.: 0-20. | Optional |
| headers | Table headers to use the human readable output (if none provided, will show all table headers). | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.Asset.ID | number | The ID of the asset. |
| Endpoint.IPAddress | Unknown | IP address of the asset. |
| QRadar.Asset.Name.Value | string | Name of the asset. |
| Endpoint.OS | number | Asset OS. |
| QRadar.Asset.AggregatedCVSSScore.Value | number | CVSSScore. |
| QRadar.Asset.AggregatedCVSSScore.LastUser | string | Last user who updated the Aggregated CVSS Score. |
| QRadar.Asset.Weight.Value | number | Asset weight. |
| QRadar.Asset.Weight.LastUser | string | Last user who updated the weight. |
| QRadar.Asset.Name.LastUser | string | Last user who updated the name. |
qradar-get-asset-by-id
Retrieves the asset by id.
Base Command
qradar-get-asset-by-id
Input
| Argument Name | Description | Required |
|---|---|---|
| asset_id | The ID of the requested asset. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.Asset.ID | number | The ID of the asset. |
| Endpoint.MACAddress | Unknown | Asset MAC address. |
| Endpoint.IPAddress | Unknown | IP address of the endpoint. |
| QRadar.Asset.ComplianceNotes.Value | string | Compliance notes. |
| QRadar.Asset.CompliancePlan.Value | string | Compliance plan. |
| QRadar.Asset.CollateralDamagePotential.Value | Unknown | Collateral damage potential. |
| QRadar.Asset.AggregatedCVSSScore.Value | number | CVSSScore. |
| QRadar.Asset.Name.Value | string | Name of the asset. |
| QRadar.Asset.GroupName | string | Name of the asset’s group. |
| Endpoint.Domain | Unknown | DNS name. |
| Endpoint.OS | Unknown | Asset OS. |
| QRadar.Asset.Weight.Value | number | Asset weight. |
| QRadar.Asset.Vulnerabilities.Value | Unknown | Vulnerabilities. |
| QRadar.Asset.Location | string | Location. |
| QRadar.Asset.Description | string | The asset description. |
| QRadar.Asset.SwitchID | number | Switch ID. |
| QRadar.Asset.SwitchPort | number | Switch port. |
| QRadar.Asset.Name.LastUser | string | Last user who updated the name. |
| QRadar.Asset.AggregatedCVSSScore.LastUser | string | Last user who updated the Aggregated CVSS Score. |
| QRadar.Asset.Weight.LastUser | string | Last user who updated the weight. |
| QRadar.Asset.ComplianceNotes.LastUser | string | Last user who updated the compliance notes. |
| QRadar.Asset.CompliancePlan.LastUser | string | Last user who updated the compliance plan. |
| QRadar.Asset.CollateralDamagePotential.LastUser | string | Last user who updated the collateral damage potential. |
| QRadar.Asset.Vulnerabilities.LastUser | string | Last user who updated the vulnerabilities. |
qradar-get-closing-reasons
Get closing reasons.
Base Command
qradar-get-closing-reasons
Input
| Argument Name | Description | Required |
|---|---|---|
| include_reserved | If true, reserved closing reasons are included in the response. Possible values are: true, false. Default is true. | Optional |
| include_deleted | If true, deleted closing reasons are included in the response. Possible values are: true, false. Default is true. | Optional |
| filter | Query to filter results. For reference, consult: https://www.ibm.com/support/knowledgecenter/en/SS42VS_7.3.1/com.ibm.qradar.doc/c_rest_api_filtering.html. | Optional |
| fields | If used, will filter all fields except for the specified ones. Use this parameter to specify which fields you would like to get back in the response. Fields that are not explicitly named are excluded. Specify subfields in brackets and multiple fields in the same object separated by commas. The filter uses QRadar’s field names. For reference, consult: https://www.ibm.com/support/knowledgecenter/SSKMKU/com.ibm.qradar.doc_cloud/9.1–siem-offense_closing_reasons-GET.html. | Optional |
| range | Range of results to return. e.g.: 0-20. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.Offense.ClosingReasons.ID | number | Closing reason ID. |
| QRadar.Offense.ClosingReasons.Name | string | Closing reason name. |
qradar-get-note
Retrieve a note for an offense.
Base Command
qradar-get-note
Input
| Argument Name | Description | Required |
|---|---|---|
| offense_id | The offense ID to retrieve the note from. | Required |
| note_id | The note ID. | Optional |
| fields | If used, will filter all fields except for the specified ones. Use this parameter to specify which fields you would like to get back in the response. Fields that are not explicitly named are excluded. Specify subfields in brackets and multiple fields in the same object separated by commas. The filter uses QRadar’s field names. For reference, consult: https://www.ibm.com/support/knowledgecenter/SSKMKU/com.ibm.qradar.doc_cloud/9.1–siem-offenses-offense_id-notes-note_id-GET.html. | Optional |
| headers | Table headers to use the human readable output (if none provided, will show all table headers). | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.Note.ID | number | Note ID. |
| QRadar.Note.Text | string | Note text. |
| QRadar.Note.CreateTime | date | The creation time of the note. |
| QRadar.Note.CreatedBy | string | The user who created the note. |
qradar-create-note
Create a note on an offense.
Base Command
qradar-create-note
Input
| Argument Name | Description | Required |
|---|---|---|
| offense_id | The offense ID to add the note to. | Required |
| note_text | The note text. | Required |
| fields | If used, will filter all fields except for the specified ones. Use this parameter to specify which fields you would like to get back in the response. Fields that are not explicitly named are excluded. Specify subfields in brackets and multiple fields in the same object separated by commas. The filter uses QRadar’s field names. For reference, consult: https://www.ibm.com/support/knowledgecenter/SSKMKU/com.ibm.qradar.doc_cloud/9.1–siem-offenses-offense_id-notes-POST.html. | Optional |
| headers | Table headers to use the human readable output (if none provided, will show all table headers). | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.Note.ID | number | Note ID. |
| QRadar.Note.Text | string | Note text. |
| QRadar.Note.CreateTime | date | The creation time of the note. |
| QRadar.Note.CreatedBy | string | The user who created the note. |
qradar-get-reference-by-name
Information about the reference set that had data added or updated. This returns the information set, but not the contained data. This feature is supported from version 8.1 and upward.
Base Command
qradar-get-reference-by-name
Input
| Argument Name | Description | Required |
|---|---|---|
| ref_name | The name of the requestered reference. | Required |
| headers | Table headers to use in the human readable output (if none provided, will show all table headers). | Optional |
| date_value | If set to true will try to convert the data values to ISO-8601 string. Possible values are: True, False. Default is False. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.Reference.Name | string | The name of the reference set. |
| QRadar.Reference.CreationTime | date | The creation time (ISO) of the reference. |
| QRadar.Reference.ElementType | string | Reference element type. |
| QRadar.Reference.NumberOfElements | number | Number of elements. |
| QRadar.Reference.TimeToLive | string | Reference time to live. |
| QRadar.Reference.TimeoutType | string | Reference timeout type. Valid values are: UNKNOWN, FIRST_SEEN, LAST_SEEN. |
| QRadar.Reference.Data | Unknown | Reference set items. |
qradar-create-reference-set
Creates a new reference set. If the provided name is already in use, this command will fail.
Base Command
qradar-create-reference-set
Input
| Argument Name | Description | Required |
|---|---|---|
| ref_name | Reference name to be created. | Required |
| element_type | The element type for the values allowed in the reference set. The allowed values are: ALN (alphanumeric), ALNIC (alphanumeric ignore case), IP (IP address), NUM (numeric), PORT (port number) or DATE. Note that date values need to be represented in milliseconds since the Unix Epoch January 1st 1970. Possible values are: ALN, ALNIC, IP, NUM, PORT, DATE. | Required |
| timeout_type | The allowed values are “FIRST_SEEN”, LAST_SEEN and UNKNOWN. The default value is UNKNOWN. Possible values are: FIRST_SEEN, LAST_SEEN, UNKNOWN. | Optional |
| time_to_live | The time to live interval, for example: “1 month” or “5 minutes”. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.Reference.CreationTime | date | Creation time of the reference set. |
| QRadar.Reference.ElementType | string | The element type for the values allowed in the reference set. The allowed values are: ALN (alphanumeric), ALNIC (alphanumeric ignore case), IP (IP address), NUM (numeric), PORT (port number) or DATE. |
| QRadar.Reference.Name | string | Name of the reference set. |
| QRadar.Reference.NumberOfElements | number | Number of elements in the created reference set. |
| QRadar.Reference.TimeoutType | string | Timeout type of the reference. The allowed values are FIRST_SEEN, LAST_SEEN and UNKNOWN. |
qradar-delete-reference-set
Deletes a reference set corresponding to the name provided.
Base Command
qradar-delete-reference-set
Input
| Argument Name | Description | Required |
|---|---|---|
| ref_name | The name of reference set to delete. | Required |
Context Output
There is no context output for this command.
qradar-create-reference-set-value
Add or update a value in a reference set.
Base Command
qradar-create-reference-set-value
Input
| Argument Name | Description | Required |
|---|---|---|
| ref_name | The name of the reference set to add or update a value in. | Required |
| value | The value/s to add or update in the reference set. Note: Date values must be represented in epoch in reference sets (milliseconds since the Unix Epoch January 1st 1970). If ‘date_value’ is set to ‘True’, then the argument will be converted from date in format: ‘%Y-%m-%dT%H:%M:%S.%f000Z’ (e.g. ‘2018-11-06T08:56:41.000000Z’) to epoch. | Required |
| source | An indication of where the data originated. The default value is ‘reference data api’. | Optional |
| date_value | If set to True, will convert ‘value’ argument from date in format: ‘%Y-%m-%dT%H:%M:%S.%f000Z’ (e.g. ‘2018-11-06T08:56:41.000000Z’) to epoch. Possible values are: True, False. Default is False. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.Reference.Name | string | The name of the reference set. |
| QRadar.Reference.CreationTime | date | The creation time (ISO) of the reference. |
| QRadar.Reference.ElementType | string | Reference element type. |
| QRadar.Reference.NumberOfElements | number | Number of elements. |
| QRadar.Reference.TimeoutType | string | Reference timeout type. One of: UNKNOWN, FIRST_SEEN, LAST_SEEN. |
qradar-update-reference-set-value
Adds or updates a value in a reference set.
Base Command
qradar-update-reference-set-value
Input
| Argument Name | Description | Required |
|---|---|---|
| ref_name | The name of the reference set to add or update a value in. | Required |
| value | A comma-separated list of values to add or update in the reference set. Date values must be represented in milliseconds since the Unix Epoch January 1st 1970. | Required |
| source | An indication of where the data originated. The default value is ‘reference data api’. | Optional |
| date_value | If set to True, will convert ‘value’ argument from date in format: ‘%Y-%m-%dT%H:%M:%S.%f000Z’ (e.g. ‘2018-11-06T08:56:41.000000Z’) to epoch. Possible values are: True, False. Default is False. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.Reference.Name | string | The name of the reference set. |
| QRadar.Reference.CreationTime | date | The creation time (ISO) of the reference. |
| QRadar.Reference.ElementType | string | Reference element type. |
| QRadar.Reference.NumberOfElements | number | Number of elements. |
| QRadar.Reference.TimeoutType | string | Reference timeout type. One of: UNKNOWN, FIRST_SEEN, LAST_SEEN |
qradar-delete-reference-set-value
Deletes a value in a reference set.
Base Command
qradar-delete-reference-set-value
Input
| Argument Name | Description | Required |
|---|---|---|
| ref_name | The name of the reference set to remove a value from. | Required |
| value | The value to remove from the reference set. | Required |
| date_value | If set to True will convert ‘value’ argument from date in format: ‘%Y-%m-%dT%H:%M:%S.%f000Z’ (e.g., ‘2018-11-06T08:56:41.000000Z’) to epoch. Possible values are: True, False. Default is False. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.Reference.Name | string | The name of the reference set. |
| QRadar.Reference.CreationTime | date | The creation time (ISO) of the reference. |
| QRadar.Reference.ElementType | string | Reference element type. |
| QRadar.Reference.NumberOfElements | number | Number of elements. |
| QRadar.Reference.TimeoutType | string | Reference timeout type. One of: UNKNOWN, FIRST_SEEN, LAST_SEEN. |
qradar-delete-reference-set-value
Deletes a value in a reference set.
Base Command
qradar-delete-reference-set-value
Input
| Argument Name | Description | Required |
|---|---|---|
| ref_name | The name of the reference set to remove a value from. | Required |
| value | The value to remove from the reference set. | Required |
| date_value | If set to True will convert ‘value’ argument from date in format: ‘%Y-%m-%dT%H:%M:%S.%f000Z’ (e.g. ‘2018-11-06T08:56:41.000000Z’) to epoch. Possible values are: True, False. Default is False. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.Reference.Name | string | The name of the reference set. |
| QRadar.Reference.CreationTime | date | The creation time (ISO) of the reference. |
| QRadar.Reference.ElementType | string | Reference element type. |
| QRadar.Reference.NumberOfElements | number | Number of elements. |
| QRadar.Reference.TimeoutType | string | Reference timeout type. One of: UNKNOWN, FIRST_SEEN, LAST_SEEN |
qradar-get-domains
Retrieve all Domains.
Base Command
qradar-get-domains
Input
| Argument Name | Description | Required |
|---|---|---|
| fields | If used, will filter all fields except for the specified ones. Use this parameter to specify which fields you would like to get back in the response. Fields that are not explicitly named are excluded. Specify subfields in brackets and multiple fields in the same object are separated by commas. The filter uses QRadar’s field names, for reference please consult: https://www.ibm.com/support/knowledgecenter/SSKMKU/com.ibm.qradar.doc_cloud/9.1–siem-offenses-offense_id-notes-note_id-GET.html. | Optional |
| range | Number of results in return. | Optional |
| filter | Query to filter offenses. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.Domains.AssetScannerIDs | Number | Array of Asset Scanner IDs. |
| QRadar.Domains.CustomProperties | String | Custom properties of the domain. |
| QRadar.Domains.Deleted | Boolean | Indicates if the domain is deleted. |
| QRadar.Domains.Description | String | Description of the domain. |
| QRadar.Domains.EventCollectorIDs | Number | Array of Event Collector IDs. |
| QRadar.Domains.FlowCollectorIDs | Number | Array of Flow Collector IDs. |
| QRadar.Domains.FlowSourceIDs | Number | Array of Flow Source IDs. |
| QRadar.Domains.ID | Number | ID of the domain. |
| QRadar.Domains.LogSourceGroupIDs | Number | Array of Log Source Group IDs. |
| QRadar.Domains.LogSourceIDs | Number | Array of Log Source IDs. |
| QRadar.Domains.Name | String | Name of the Domain. |
| QRadar.Domains.QVMScannerIDs | Number | Array of QVM Scanner IDs. |
| QRadar.Domains.TenantID | Number | ID of the Domain tenant. |
qradar-get-domain-by-id
Retrieves Domain information By ID.
Base Command
qradar-get-domain-by-id
Input
| Argument Name | Description | Required |
|---|---|---|
| id | ID of the domain. | Required |
| fields | If used, will filter all fields except for the specified ones. Use this parameter to specify which fields you would like to get back in the response. Fields that are not explicitly named are excluded. Specify subfields in brackets and multiple fields in the same object are separated by commas. The filter uses QRadar’s field names, for reference please consult: https://www.ibm.com/support/knowledgecenter/SSKMKU/com.ibm.qradar.doc_cloud/9.1–siem-offenses-offense_id-notes-note_id-GET.html. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.Domains.AssetScannerIDs | Number | Array of Asset Scanner IDs. |
| QRadar.Domains.CustomProperties | String | Custom properties of the domain. |
| QRadar.Domains.Deleted | Boolean | Indicates if the domain is deleted. |
| QRadar.Domains.Description | String | Description of the domain. |
| QRadar.Domains.EventCollectorIDs | Number | Array of Event Collector IDs. |
| QRadar.Domains.FlowCollectorIDs | Number | Array of Flow Collector IDs. |
| QRadar.Domains.FlowSourceIDs | Number | Array of Flow Source IDs. |
| QRadar.Domains.ID | Number | ID of the domain. |
| QRadar.Domains.LogSourceGroupIDs | Number | Array of Log Source Group IDs. |
| QRadar.Domains.LogSourceIDs | Number | Array of Log Source IDs. |
| QRadar.Domains.Name | String | Name of the Domain. |
| QRadar.Domains.QVMScannerIDs | Number | Array of QVM Scanner IDs. |
| QRadar.Domains.TenantID | Number | ID of the Domain tenant. |
qradar-upload-indicators
Uploads indicators from Demisto to QRadar.
Base Command
qradar-upload-indicators
Input
| Argument Name | Description | Required |
|---|---|---|
| ref_name | The name of the reference set to add or update a value in. To create a new reference set, you need to set the element type. | Required |
| element_type | The element type for the values permitted in the reference set. Only required when creating a new reference set. The valid values are: ALN (alphanumeric), ALNIC (alphanumeric ignore case), IP (IP address), NUM (numeric), PORT (port number) or DATE. Note that date values need to be represented in milliseconds since the Unix Epoch January 1st 1970. Possible values are: ALN, ALNIC, IP, NUM, PORT, DATE. | Optional |
| timeout_type | The timeout_type can be “FIRST_SEEN”, “LAST_SEEN”, or “UNKNOWN”. The default value is UNKNOWN. Only required for creating a new reference set. Possible values are: FIRST_SEEN, LAST_SEEN, UNKNOWN. | Optional |
| time_to_live | The time to live interval, for example: “1 month” or “5 minutes”. Only required when creating a new reference set. | Optional |
| query | The query for getting indicators. | Required |
| limit | The maximum number of indicators to return. The default value is 1000. Default is 1000. | Optional |
| page | The page from which to get the indicators. Default is 0. | Optional |
Context Output
There is no context output for this command.
qradar-ips-source-get
Get Source IPs
Base Command
qradar-ips-source-get
Input
| Argument Name | Description | Required |
|---|---|---|
| source_ip | Comma separated list. Source IPs to retrieve their data, E.g “1.1.1.1,1.1.1.1”. | Optional |
| filter | Query to filter IPs. E.g, filter=source_ip="1.1.1.1". For reference please consult: https://www.ibm.com/support/knowledgecenter/SS42VS_SHR/com.ibm.qradarapi.doc/c_rest_api_filtering.html. |
Optional |
| fields | If used, will filter all fields except for the ones specified. Use this argument to specify which fields should be returned in the response. Fields that are not named are excluded. Specify subfields in brackets and multiple fields in the same object separated by commas. The filter uses QRadar’s field names, for reference, consult: https://ibmsecuritydocs.github.io/qradar_api_14.0/14.0–siem-source_addresses-GET.html. | Optional |
| range | Range of results to return. e.g.: 0-20. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.SourceIP.ID | Number | The ID of the destination address. |
| QRadar.SourceIP.DomainID | String | The ID of associated domain. |
| QRadar.SourceIP.EventFlowCount | Number | The number of events and flows that are associated with the destination address. |
| QRadar.SourceIP.FirstEventFlowSeen | Date | Date when the first event or flow was seen. |
| QRadar.SourceIP.LastEventFlowSeen | Date | Date when the last event or flow was seen. |
| QRadar.SourceIP.SourceIP | String | The IP address. |
| QRadar.SourceIP.Magnitude | Number | The magnitude of the destination address. |
| QRadar.SourceIP.Network | String | The network of the destination address. |
| QRadar.SourceIP.OffenseIDs | Unknown | List of offense IDs the destination address is part of. |
| QRadar.SourceIP.LocalDestinationAddressIDs | Unknown | List of local destination address IDs associated with the source address. |
Command example
!qradar-ips-source-get filter=`source_ip="1.1.1.1"` range=0-2
Context Example
{
"QRadar": {
"SourceIP": {
"DomainID": 0,
"EventFlowCount": 654,
"FirstEventFlowSeen": "2023-07-26T14:31:44.753000+00:00",
"ID": 31,
"LastEventFlowSeen": "2023-07-26T15:31:06.386000+00:00",
"LocalDestinationAddressIDs": [
64
],
"Magnitude": 0,
"Network": "Net-10-172-192.Net_192_168_0_0",
"OffenseIDs": [
14
],
"SourceIP": "1.1.1.1"
}
}
}
Human Readable Output
Source IPs
DomainID EventFlowCount FirstEventFlowSeen ID LastEventFlowSeen LocalDestinationAddressIDs Magnitude Network OffenseIDs SourceIP 0 654 2023-07-26T14:31:44.753000+00:00 31 2023-07-26T15:31:06.386000+00:00 64 0 Net-10-172-192.Net_192_168_0_0 14 1.1.1.1
qradar-ips-local-destination-get
Get Source IPs
Base Command
qradar-ips-local-destination-get
Input
| Argument Name | Description | Required |
|---|---|---|
| local_destination_ip | Comma separated list. Local destination IPs to retrieve their data, E.g “1.1.1.1,1.1.1.1”. | Optional |
| filter | Query to filter IPs. E.g, filter=local_destination_ip="1.1.1.1" For reference please consult: https://www.ibm.com/support/knowledgecenter/SS42VS_SHR/com.ibm.qradarapi.doc/c_rest_api_filtering.html. |
Optional |
| fields | If used, will filter all fields except for the ones specified. Use this argument to specify which fields should be returned in the response. Fields that are not named are excluded. Specify subfields in brackets and multiple fields in the same object separated by commas. The filter uses QRadar’s field names, for reference, consult: https://ibmsecuritydocs.github.io/qradar_api_14.0/14.0–siem-local_destination_addresses-GET.html. | Optional |
| range | Range of results to return. e.g.: 0-20. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.LocalDestinationIP.ID | Number | The ID of the destination address. |
| QRadar.LocalDestinationIP.DomainID | String | The ID of associated domain. |
| QRadar.LocalDestinationIP.EventFlowCount | Number | The number of events and flows that are associated with the destination address. |
| QRadar.LocalDestinationIP.FirstEventFlowSeen | Date | Date when the first event or flow was seen. |
| QRadar.LocalDestinationIP.LastEventFlowSeen | Date | Date when the last event or flow was seen. |
| QRadar.LocalDestinationIP.LocalDestinationIP | String | The IP address. |
| QRadar.LocalDestinationIP.Magnitude | Number | The magnitude of the destination address. |
| QRadar.LocalDestinationIP.Network | String | The network of the destination address. |
| QRadar.LocalDestinationIP.OffenseIDs | Unknown | List of offense IDs the destination address is part of. |
| QRadar.LocalDestinationIP.SourceAddressIDs | Unknown | List of source address IDs associated with the destination address. |
Command example
!qradar-ips-local-destination-get filter=`local_destination_ip="1.1.1.1"`
Human Readable Output
Local Destination IPs
No entries.
qradar-search-retrieve-events
Polling command to search for events of a specific offense.
Base Command
qradar-search-retrieve-events
Input
| Argument Name | Description | Required |
|---|---|---|
| offense_id | The ID of the offense to retrieve. Mutually exclusive with query_expression. | Optional |
| events_limit | The number of events to return. Mutually exclusive with query_expression. | Optional |
| events_columns | Comma separated list of columns to return. Mutually exclusive with query_expression. | Optional |
| fetch_mode | The mode to use when fetching events. Mutually exclusive with query_expression. Possible values are: Fetch With All Events, Fetch Correlation Events Only. | Optional |
| start_time | The start time of the search. Mutually exclusive with query_expression. | Optional |
| query_expression | The AQL query to execute. Mutually exclusive with the other arguments. | Optional |
| interval_in_seconds | The interval in seconds to use when polling events. | Optional |
| search_id | The search id to query the results. | Optional |
| retry_if_not_all_fetched | Whenever set to true, the command retries to fetch all events if the number of events fetched is less than event_count. Possible values are: true, false. |
Optional |
| polling | Wait for search results. Possible values are: true, false. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.SearchEvents.ID | Unknown | The search id of the query. |
| QRadar.SearchEvents.Events | Unknown | The events from QRadar search. |
| QRadar.SearchEvents.Status | Unknown | The status of the search (‘wait’, ‘partial’, ‘success’). |
Command example
!qradar-search-retrieve-events offense_id=14
Context Example
{
"QRadar": {
"SearchEvents": {
"ID": "9c2c18a8-5e06-4edb-bc26-53ad44421148",
"Status": "wait"
}
}
}
Human Readable Output
Search ID: 9c2c18a8-5e06-4edb-bc26-53ad44421148
qradar-remote-network-cidr-create
Create remote network CIDRs.
Base Command
qradar-remote-network-cidr-create
Input
| Argument Name | Description | Required |
|---|---|---|
| cidrs | An input list of CIDRs to add to QRadar (can be obtained automatically from EDL integrations and playbook). Multiple values in the same object are separated by commas. A CIDR or query is required. | Optional |
| query | The query for getting indicators from Cortex XSOAR. A CIDR or query is required. | Optional |
| name | A CIDR (remote network) name that will be displayed for all uploaded values in QRadar. | Required |
| description | Description that will be displayed and associated with all the newly uploaded CIDRs in QRadar. | Required |
| group | The exact name of the remote network group that CIDRs should be associated with as it appears in QRadar. A single group can be assigned to each create command. A new remote network group can be created in QRadar by giving a new unique remote network group name (that does not already exist in QRadar remote networks). | Required |
| fields | Use this parameter to specify which fields you would like to get back in the response. Fields that are not named are excluded from the output. Specify subfields in brackets, and multiple fields in the same object are separated by commas. The possible fields are id, group, name, CIDR, and description. | Optional |
Context Output
There is no context output for this command.
qradar-remote-network-cidr-list
Retrieves the list of staged remote networks.
Base Command
qradar-remote-network-cidr-list
Input
| Argument Name | Description | Required |
|---|---|---|
| limit | The maximum number of results to return. The default is 50. | Optional |
| page | The page offset. | Optional |
| page_size | Maximum number of results to retrieve in each page. | Optional |
| group | The name of the remote network group that the CIDRs are associated with, as it appears in QRadar. | Optional |
| id | ID of the CIDR (remote network). | Optional |
| name | The name of the CIDRs (remote network) as it appears in QRadar. | Optional |
| filter | Additional options to filter results using a query expression. | Optional |
| fields | Use this parameter to specify which fields you would like to get back in the response. Fields that are not named are excluded. By default, this argument returns all fields (id, name, cidrs, group, description). | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.RemoteNetworkCIDR | Number | A list of all the retrieved CIDRs. |
| QRadar.RemoteNetworkCIDR.id | Number | ID of each CIDR remote network that is part of the group. |
| QRadar.RemoteNetworkCIDR.name | String | The associated CIDR name as it appears in QRadar. |
| QRadar.RemoteNetworkCIDR.description | String | The associated CIDR description as it appears in QRadar. |
qradar-remote-network-cidr-delete
Deletes an existing staged remote network.
Base Command
qradar-remote-network-cidr-delete
Input
| Argument Name | Description | Required |
|---|---|---|
| id | ID that is used to locate the staged remote network that users want to remove from QRadar. | Required |
Context Output
There is no context output for this command.
qradar-remote-network-cidr-update
Updates an existing staged remote network.
Base Command
qradar-remote-network-cidr-update
Input
| Argument Name | Description | Required |
|---|---|---|
| id | The ID that is associated with the CIDR object that needs to be modified. | Required |
| name | The CIDR name in QRadar. If the CIDR name should be changed, it can be inserted here. | Required |
| cidrs | An input list of CIDRs to add to QRadar (can be obtained automatically from EDL integrations and playbook). Multiple values in the same object are separated by commas. A CIDR or query is required. | Optional |
| query | The query for getting indicators from Cortex XSOAR. A CIDR or query is required. | Optional |
| description | CIDR associated description presented in QRadar. If the CIDR description should be changed, it can be inserted here. | Required |
| group | The remote network group that CIDRs should belong to. If the CIDR-associated group should be changed, it can be inserted here. | Required |
| fields | Use this parameter to specify which fields you would like to get back in the response. Fields that are not named are excluded. Specify subfields in brackets, and multiple fields in the same object are separated by commas. The possible fields are id,group,name,cidr,description. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.RemoteNetworkCIDR | Number | A list of all the CIDR ranges that were changed. |
| QRadar.RemoteNetworkCIDR.id | Number | The associated CIDR ID. |
| QRadar.RemoteNetworkCIDR.name | String | The associated CIDR name. |
| QRadar.RemoteNetworkCIDR.group | String | The group to which the remote network belongs. |
| QRadar.RemoteNetworkCIDR.description | String | The description of the remote network. |
qradar-remote-network-deploy-execution
Executes a deployment.
Potentially harmful: This API command executes any waiting system deployments in QRadar within the same deployment type and hosts defined.
Base Command
qradar-remote-network-deploy-execution
Input
| Argument Name | Description | Required |
|---|---|---|
| host_ip | The IP of QRadar console host. | Required |
| status | The deployment status. Must be in capital letters (“INITIATING”). Possible values are: INITIATING. | Optional |
| deployment_type | The deployment type. Must be in capital letters (“INCREMENTAL” or “FULL”). Possible values are: INCREMENTAL, FULL. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.deploy.status | String | The deployment status (INITIALIZING, IN_PROGRESS, COMPLETE). |
qradar-log-source-extensions-list
Retrieves a list of log source extensions.
Base Command
qradar-log-source-extensions-list
Input
| Argument Name | Description | Required |
|---|---|---|
| range | Range of results to return (e.g.: 0-20, 3-5, 3-3). Default is 0-49. | Optional |
| filter | Query by which to filter disconnected log collectors, e.g., “protocol=udp”. For reference, see: https://www.ibm.com/support/knowledgecenter/SS42VS_SHR/com.ibm.qradarapi.doc/c_rest_api_filtering.html. | Optional |
| fields | Comma-separated list of fields to retrieve in the response. Fields that are not explicitly named are excluded. E.g., “id,name,protocol”. Specify subfields in brackets and multiple fields in the same object separated by commas. For a full list of available fields, see: https://ibmsecuritydocs.github.io/qradar_api_20.0/20.0–config-event_sources-log_source_management-log_source_extensions-GET.html. | Optional |
| id | ID of a specific disconnected log collector. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.LogSourceExtension.Name | String | The name of the log source extension. |
| QRadar.LogSourceExtension.Description | String | The description of the log source extension. |
| QRadar.LogSourceExtension.ID | Number | The ID of the extension. |
| QRadar.LogSourceExtension.UUID | String | The UUID string of the log source extension. |
qradar-log-source-delete
Deletes a log source by ID or name. One of the arguments must be provided.
Base Command
qradar-log-source-delete
Input
| Argument Name | Description | Required |
|---|---|---|
| id | The ID of the log source to be deleted. If this argument is not provided, name must be provided. | Optional |
| name | The unique name of the log source to be deleted. If this argument is not provided, the ID must be provided. | Optional |
Context Output
There is no context output for this command.
qradar-wincollect-destinations-list
Retrieves a list of WinCollect destinations.
In order to get wincollect_internal_destination_ids - filter internal=true needs to be used
In order to get wincollect_external_destination_ids - filter internal=false needs to be used.
Base Command
qradar-wincollect-destinations-list
Input
| Argument Name | Description | Required |
|---|---|---|
| range | Range of results to return (e.g.: 0-20, 3-5, 3-3). Default is 0-49. | Optional |
| filter | Query by which to filter wincollect destinations, e.g., “internal=true”. For reference, see: https://www.ibm.com/support/knowledgecenter/SS42VS_SHR/com.ibm.qradarapi.doc/c_rest_api_filtering.html. | Optional |
| fields | Comma-separated list of fields to retrieve in the response. Fields that are not explicitly named are excluded. E.g., “id,name,host”. Specify subfields in brackets and multiple fields in the same object separated by commas. For a full list of available fields, see: https://ibmsecuritydocs.github.io/qradar_api_20.0/20.0–config-event_sources-wincollect-wincollect_destinations-GET.html. | Optional |
| id | ID of a specific WinCollect destination. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.WinCollectDestination.ID | Number | The ID of the WinCollect destination. |
| QRadar.WinCollectDestination.Name | String | The name of the WinCollect destination. |
| QRadar.WinCollectDestination.Host | String | The IP or hostname of the WinCollect destination. WinCollect agents that use this destination send syslog event data to this host. |
| QRadar.WinCollectDestination.TlsCertificate | String | The TLS Certificate of the WinCollect destination. |
| QRadar.WinCollectDestination.Port | Number | The listen port of the WinCollect destination. WinCollect agents that use this destination send syslog event data to this port. |
| QRadar.WinCollectDestination.TransportProtocol | String | The protocol that is used to send event data to this WinCollect destination. Possible values are TCP or UDP. |
| QRadar.WinCollectDestination.IsInternal | Boolean | Set to “true” if the destination corresponds to a QRadar event collector process from this deployment; otherwise, it is set to false if it is any other host. |
| QRadar.WinCollectDestination.EventRateThrottle | Number | The events-per-second rate that is used to throttle the event flow to this destination. |
qradar-log-source-create
Creates a new log source.
Base Command
qradar-log-source-create
Input
| Argument Name | Description | Required |
|---|---|---|
| name | The unique name of the log source. | Required |
| protocol_type_id | The type of protocol that is used by the log source. Must correspond to an existing protocol type. | Required |
| type_id | The type of the log source. Must correspond to an existing log source type. | Required |
| protocol_parameters | The list of protocol parameters corresponding with the selected protocol type ID. The syntax for this argument should follow: protocol_parameters=”name_1=value_1,name_2=value_2,…,name_n=value_n” where each name should correspond to a name of a protocol parameter from the protocol type and each value should fit the type of the protocol parameter. The command qradar-log-source-protocol-types-list can be used to list all available protocol types. | Required |
| target_event_collector_id | The ID of the event collector where the log source sends its data. The ID must correspond to an existing event collector. | Required |
| sending_ip | The IP of the system which the log source is associated to, or fed by. | Optional |
| description | The description of the log source. | Optional |
| coalesce_events | Determines if events collected by this log source are coalesced based on common properties. If each individual event is stored, then the condition is set to false. Defaults to true. | Optional |
| enabled | Determines if the log source is enabled. Defaults to true. | Optional |
| parsing_order | The order in which log sources will parse if multiples exist with a common identifier. | Optional |
| group_ids | The set of log source group IDs this log source is a member of. Each ID must correspond to an existing log source group. The command qradar-log-sources-groups-list can be used to list all available groups. See the Log Source Group API (https://ibmsecuritydocs.github.io/qradar_api_20.0/20.0–config-event_sources-log_source_management-log_source_groups-id-GET.html). | Optional |
| credibility | On a scale of 0-10, the amount of credibility that the QRadar administrator places on this log source. | Optional |
| store_event_payload | If the payloads of events that are collected by this log source are stored, the condition is set to ‘true’. If only the normalized event records are stored, then the condition is set to ‘false’. | Optional |
| disconnected_log_collector_id | The ID of the disconnected log collector where this log source will run. The ID must correspond to an existing disconnected log collector. | Optional |
| language_id | The language of the events that are being processed by this log source. Must correspond to an existing log source language. | Optional |
| requires_deploy | Set to ‘true’ if you need to deploy changes to enable the log source for use; otherwise, set to ‘false’ if the log source is already active. | Optional |
| wincollect_internal_destination_id | The internal WinCollect destination for this log source, if applicable. Log sources without an associated WinCollect agent have a null value. Must correspond to an existing WinCollect destination. | Optional |
| wincollect_external_destination_ids | The set of external WinCollect destinations for this log source, if applicable. Log sources without an associated WinCollect agent have a null value. Each ID must correspond to an existing WinCollect destination. | Optional |
| gateway | If the log source is configured as a gateway, the condition is set to ‘true’; otherwise, the condition is set to ‘false’. A gateway log source is a standalone protocol configuration. The log source receives no events itself, and serves as a host for a protocol configuration that retrieves event data to feed other log sources. It acts as a “gateway” for events from multiple systems to enter the event pipeline. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.LogSource.SendingIP | String | IP of the system which the log source is associated with, or fed by. |
| QRadar.LogSource.Internal | Boolean | Whether log source is internal. |
| QRadar.LogSource.ProtocolParameters | Unknown | Protocol parameters. |
| QRadar.LogSource.Description | String | Description of the log source. |
| QRadar.LogSource.Enabled | Boolean | Whether log source is enabled. |
| QRadar.LogSource.GroupIDs | Number | Log source group IDs. |
| QRadar.LogSource.Credibility | Number | Credibility of the log source. |
| QRadar.LogSource.ID | Number | ID of the log source. |
| QRadar.LogSource.ProtocolTypeID | Number | Protocol type used by log source. |
| QRadar.LogSource.CreationDate | Date | Date when log source was created. |
| QRadar.LogSource.Name | String | Name of the log source. |
| QRadar.LogSource.AutoDiscovered | Boolean | Whether log source was auto discovered. |
| QRadar.LogSource.ModifiedDate | Date | Date when log source was last modified. |
| QRadar.LogSource.TypeID | Number | The log source type. |
| QRadar.LogSource.LastEventTime | Date | Date when the last event was received by the log source. |
| QRadar.LogSource.Gateway | Boolean | Whether log source is configured as a gateway. |
| QRadar.LogSource.Status | unknown | Status of the log source. |
| QRadar.LogSource.TargetEventCollectorID | Number | The ID of the event collector where the log source sends its data. |
qradar-log-source-languages-list
Retrieves a list of log source languages.
Base Command
qradar-log-source-languages-list
Input
| Argument Name | Description | Required |
|---|---|---|
| range | Range of results to return (e.g.: 0-20, 3-5, 3-3). Default is 0-49. | Optional |
| filter | Query by which to filter disconnected log collectors, e.g., “protocol=udp”. For reference, see: https://www.ibm.com/support/knowledgecenter/SS42VS_SHR/com.ibm.qradarapi.doc/c_rest_api_filtering.html. | Optional |
| fields | Comma-separated list of fields to retrieve in the response. Fields that are not explicitly named are excluded. E.g., “id,name,protocol”. Specify subfields in brackets and multiple fields in the same object separated by commas. For a full list of available fields, see: https://ibmsecuritydocs.github.io/qradar_api_20.0/20.0–config-event_sources-log_source_management-log_source_languages-GET.html. | Optional |
| id | ID of a specific disconnected log collector. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.LogSourceLanguage.ID | Number | The ID of the language. This ID does not change across deployments. |
| QRadar.LogSourceLanguage.Name | String | The display name of the language. |
qradar-log-source-protocol-types-list
Retrieves the list of protocol types.
Base Command
qradar-log-source-protocol-types-list
Input
| Argument Name | Description | Required |
|---|---|---|
| range | Range of results to return (e.g.: 0-20, 3-5, 3-3). Default is 0-49. | Optional |
| filter | Query by which to filter disconnected log collectors, e.g., “protocol=udp”. For reference, see: https://www.ibm.com/support/knowledgecenter/SS42VS_SHR/com.ibm.qradarapi.doc/c_rest_api_filtering.html. | Optional |
| fields | Comma-separated list of fields to retrieve in the response. Fields that are not explicitly named are excluded. E.g., “id,name,protocol_parameters”. Specify subfields in brackets and multiple fields in the same object separated by commas. For a full list of available fields, see: https://ibmsecuritydocs.github.io/qradar_api_20.0/20.0–config-event_sources-log_source_management-protocol_types-GET.html. | Optional |
| id | ID of a specific disconnected log collector. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.LogSourceProtocolType.GatewaySupported | Boolean | If this protocol type can be configured for a gateway log source, the condition is set to ‘true’; otherwise, the condition is set to ‘false’. A gateway log source is a standalone protocol configuration. The log source receives no events itself, and serves as a host for a protocol configuration that retrieves event data to feed other log sources. It acts as a ‘gateway’ for events from multiple systems to enter the event pipeline. Not all protocol types can be used as a gateway if they don’t support collecting event data from multiple sources. |
| QRadar.LogSourceProtocolType.ID | Number | The ID of the protocol type. |
| QRadar.LogSourceProtocolType.Inbound | Boolean | Indicates whether this is an inbound protocol. |
| QRadar.LogSourceProtocolType.LatestVersion | String | The latest version available of the protocol type component. |
| QRadar.LogSourceProtocolType.Name | String | The unique name of the protocol type. |
| QRadar.LogSourceProtocolType.ParameterGroups.id | Number | The ID of the protocol parameter group. |
| QRadar.LogSourceProtocolType.ParameterGroups.name | String | The name of the protocol parameter group. |
| QRadar.LogSourceProtocolType.ParameterGroups.required | Boolean | If at least one parameter in this group must be set, the condition is set to true; otherwise, the condition is set to false. |
| QRadar.LogSourceProtocolType.Parameters.allowed_values.name | String | An allowed value for the name of the parameter. |
| QRadar.LogSourceProtocolType.Parameters.allowed_values.value | String | An allowed value for the value of the parameter. |
| QRadar.LogSourceProtocolType.Parameters.default_value | String | The optional default parameter value. |
| QRadar.LogSourceProtocolType.Parameters.description | String | The description of the parameter. |
| QRadar.LogSourceProtocolType.Parameters.group_id | Number | The ID of the protocol parameter group that this parameter belongs to. The group_id is optional. |
| QRadar.LogSourceProtocolType.Parameters.id | Number | The ID of the parameter. |
| QRadar.LogSourceProtocolType.Parameters.label | String | The label of the parameter. |
| QRadar.LogSourceProtocolType.Parameters.max_length | Number | The maximum length of the parameter value for the following parameter types: STRING, TEXT, HOST, PASSWORD, REGEX. The max_length is optional. |
| QRadar.LogSourceProtocolType.Parameters.max_value | String | The maximum of the parameter value for the following parameter types: INTEGER, REAL, DATE, TIME, DATETIME, INTERVAL. The max_value is optional. |
| QRadar.LogSourceProtocolType.Parameters.min_length | Number | The minimum length of the parameter value for the following parameter types: STRING, TEXT, HOST, PASSWORD, REGEX. The max_length is optional. |
| QRadar.LogSourceProtocolType.Parameters.min_value | String | The minimum of the parameter value for the following parameter types: INTEGER, REAL, DATE, TIME, DATETIME, INTERVAL. The max_value is optional. |
| QRadar.LogSourceProtocolType.Parameters.name | String | The name of the parameter. |
| QRadar.LogSourceProtocolType.Parameters.pattern | String | An optional Java regex pattern restriction on the parameter value for the following parameter types: STRING, TEXT, HOST, PASSWORD. |
| QRadar.LogSourceProtocolType.Parameters.pattern_description | String | The description of the pattern of the parameter. |
| QRadar.LogSourceProtocolType.Parameters.required | Boolean | If the parameter is mandatory, the condition is set to true; otherwise, the condition is set to false. |
| QRadar.LogSourceProtocolType.Parameters.rules.affected_property | String | The affected property. For possible values visit: https://ibmsecuritydocs.github.io/qradar_api_20.0/20.0–config-event_sources-log_source_management-protocol_types-GET.html |
| QRadar.LogSourceProtocolType.Parameters.rules.affected_property_value | String | The value to be applied to the affected parameter when the rule is triggered. For further info visit: https://ibmsecuritydocs.github.io/qradar_api_20.0/20.0–config-event_sources-log_source_management-protocol_types-GET.html |
| QRadar.LogSourceProtocolType.Parameters.rules.parameter_id | Number | The ID of the parameter affected by the rule. |
| QRadar.LogSourceProtocolType.Parameters.rules.trigger_parameter_id | Number | The ID of the trigger parameter. |
| QRadar.LogSourceProtocolType.Parameters.rules.trigger_pattern | String | The pattern that triggers the rule. For example, if the value of trigger_parameter_id matches the regular expression of this field, the rule triggers. |
| QRadar.LogSourceProtocolType.Parameters.type | String | The type of the parameter. Possible values are: STRING, TEXT, INTEGER, REAL, BOOLEAN, DATE, TIME, DATETIME, INTERVAL, HOST, PASSWORD, REGEX. |
| QRadar.LogSourceProtocolType.TestingCapabilities.can_accept_sample_events | Boolean | Indicates whether the protocol type can accept sample events (only applicable to inbound protocol types). |
| QRadar.LogSourceProtocolType.TestingCapabilities.can_collect_events | Boolean | Indicates whether the protocol type can collect test events. |
| QRadar.LogSourceProtocolType.TestingCapabilities.testable | Boolean | Indicates whether the protocol type is testable. |
| QRadar.LogSourceProtocolType.Version | String | The version of the protocol type component. |
qradar-disconnected-log-collectors-list
Retrieves a list of disconnected log collectors.
Base Command
qradar-disconnected-log-collectors-list
Input
| Argument Name | Description | Required |
|---|---|---|
| range | Range of results to return (e.g.: 0-20, 3-5, 3-3). Default is 0-49. | Optional |
| filter | Query by which to filter disconnected log collectors, e.g., “protocol=udp”. For reference, see: https://www.ibm.com/support/knowledgecenter/SS42VS_SHR/com.ibm.qradarapi.doc/c_rest_api_filtering.html. | Optional |
| fields | Comma-separated list of fields to retrieve in the response. Fields that are not explicitly named are excluded. E.g., “id,name,protocol”. Specify subfields in brackets and multiple fields in the same object separated by commas. For a full list of available fields, see: https://ibmsecuritydocs.github.io/qradar_api_20.0/20.0–config-event_sources-disconnected_log_collectors-GET.html. | Optional |
| id | ID of a specific disconnected log collector. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.DisconnectedLogCollector.ID | Number | The ID of the disconnected log collector. |
| QRadar.DisconnectedLogCollector.Name | String | The name of the disconnected log Collector. |
| QRadar.DisconnectedLogCollector.Description | String | The description of the disconnected log collector. |
| QRadar.DisconnectedLogCollector.Protocol | String | The transport protocol used by the disconnected log collector to send events to QRadar. Possible values are TLS and UDP. |
| QRadar.DisconnectedLogCollector.UUID | String | The UUID of the disconnected log collector. |
| QRadar.DisconnectedLogCollector.Version | String | The version of the disconnected log collector. |
qradar-log-source-update
Updates an exising log source.
Base Command
qradar-log-source-update
Input
| Argument Name | Description | Required |
|---|---|---|
| id | The ID of the log source. | Required |
| name | The unique name of the log source. | Optional |
| protocol_type_id | The type of protocol that is used by the log source. Must correspond to an existing protocol type. | Optional |
| type_id | The type of the log source. Must correspond to an existing log source type. | Optional |
| protocol_parameters | The list of protocol parameters corresponding with the selected protocol type ID. The syntax for this argument should follow: protocol_parameters=”name_1=value_1,name_2=value_2,…,name_n=value_n” where each name should correspond to a name of a protocol parameter from the protocol type and each value should fit the type of the protocol parameter. The command qradar-log-source-protocol-types-list can be used to list all available protocol types. | Optional |
| target_event_collector_id | The ID of the event collector where the log source sends its data. The ID must correspond to an existing event collector. | Optional |
| sending_ip | The IP of the system which the log source is associated to, or fed by. | Optional |
| description | The description of the log source. | Optional |
| coalesce_events | Determines if events collected by this log source are coalesced based on common properties. If each individual event is stored, then the condition is set to false. Defaults to true. | Optional |
| enabled | Determines if the log source is enabled. Defaults to true. | Optional |
| parsing_order | The order in which log sources will parse if multiples exist with a common identifier. | Optional |
| group_ids | The set of log source group IDs this log source is a member of. Each ID must correspond to an existing log source group. The command qradar-log-sources-groups-list can be used to list all available groups. See the Log Source Group API (https://ibmsecuritydocs.github.io/qradar_api_20.0/20.0–config-event_sources-log_source_management-log_source_groups-id-GET.html). | Optional |
| credibility | On a scale of 0-10, the amount of credibility that the QRadar administrator places on this log source. | Optional |
| store_event_payload | If the payloads of events that are collected by this log source are stored, the condition is set to ‘true’. If only the normalized event records are stored, then the condition is set to ‘false’. | Optional |
| disconnected_log_collector_id | The ID of the disconnected log collector where this log source will run. The ID must correspond to an existing disconnected log collector. | Optional |
| language_id | The language of the events that are being processed by this log source. Must correspond to an existing log source language. | Optional |
| requires_deploy | Set to ‘true’ if you need to deploy changes to enable the log source for use; otherwise, set to ‘false’ if the log source is already active. | Optional |
| wincollect_internal_destination_id | The internal WinCollect destination for this log source, if applicable. Log sources without an associated WinCollect agent have a null value. Must correspond to an existing WinCollect destination. | Optional |
| wincollect_external_destination_ids | The set of external WinCollect destinations for this log source, if applicable. Log Sources without an associated WinCollect agent have a null value. Each ID must correspond to an existing WinCollect destination. | Optional |
| gateway | If the log source is configured as a gateway, the condition is set to ‘true’; otherwise, the condition is set to ‘false’. A gateway log source is a standalone protocol configuration. The log source receives no events itself, and serves as a host for a protocol configuration that retrieves event data to feed other log sources. It acts as a “gateway” for events from multiple systems to enter the event pipeline. | Optional |
Context Output
There is no context output for this command.
qradar-log-source-types-list
Retrieves a list of log sources types.
Base Command
qradar-log-source-types-list
Input
| Argument Name | Description | Required |
|---|---|---|
| range | Range of results to return (e.g.: 0-20, 3-5, 3-3). Default is 0-49. | Optional |
| filter | Query by which to filter disconnected log collectors, e.g., “protocol=udp”. For reference, see: https://www.ibm.com/support/knowledgecenter/SS42VS_SHR/com.ibm.qradarapi.doc/c_rest_api_filtering.html. | Optional |
| fields | Comma-separated list of fields to retrieve in the response. Fields that are not explicitly named are excluded. E.g., “id,name,protocol”. Specify subfields in brackets and multiple fields in the same object separated by commas. For a full list of available fields, see: https://ibmsecuritydocs.github.io/qradar_api_20.0/20.0–config-event_sources-log_source_management-log_source_types-GET.html. | Optional |
| id | ID of a specific disconnected log collector. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.LogSourceTypesList.Custom | Boolean | The condition is set to true if this is a custom log source type; otherwise, the condition is set to false. |
| QRadar.LogSourceTypesList.DefaultProtocolID | Number | The ID of the default protocol type for this log source type. The ID must correspond to an existing protocol type. See the Protocol Type API (/api/config/event_sources/log_source_management/protocol_types/). |
| QRadar.LogSourceTypesList.ID | Number | The ID of the log source type. |
| QRadar.LogSourceTypesList.Internal | Boolean | The condition is set to true if the log source type is an internal log source type (for example, System Notification, SIM Audit, Asset Profiler, and so on) for which log sources cannot be created, edited, or deleted. If this is a user configurable log source type, the condition is set to false. |
| QRadar.LogSourceTypesList.LatestVersion | String | The latest available version of the log source type component. |
| QRadar.LogSourceTypesList.LogSourceExtensionID | Number | The log source extension that is associated with the log source type. The ID must correspond to an existing log source extension or be set to null. See the Log Source Extension API (/api/config/event_sources/log_source_management/log_source_extensions/). |
| QRadar.LogSourceTypesList.Name | String | The unique name of the log source type. The name is not localized. |
| QRadar.LogSourceTypesList.protocol_types.documented | Boolean | Indicates whether the protocol is documented/fully supported for this log source type. |
| QRadar.LogSourceTypesList.protocol_types.protocol_id | Number | ID of the protocol type. |
| QRadar.LogSourceTypesList.supported_language_ids | List | The IDs of the languages supported by this log source type. Each ID must correspond to an existing log source language. See the Log Source Language API: https://ibmsecuritydocs.github.io/qradar_api_20.0/20.0–config-event_sources-log_source_management-log_source_languages-id-GET.html |
| QRadar.LogSourceTypesList.uuid | String | A UUID string of the log source type. |
| QRadar.LogSourceTypesList.version | String | The log source type plugin version. |
qradar-log-source-groups-list
Retrieves a list of log source languages.
Base Command
qradar-log-source-groups-list
Input
| Argument Name | Description | Required |
|---|---|---|
| range | Range of results to return (e.g.: 0-20, 3-5, 3-3). Default is 0-49. | Optional |
| filter | Query by which to filter disconnected log collectors, e.g., “protocol=udp”. For reference, see: https://www.ibm.com/support/knowledgecenter/SS42VS_SHR/com.ibm.qradarapi.doc/c_rest_api_filtering.html. | Optional |
| fields | Comma-separated list of fields to retrieve in the response. Fields that are not explicitly named are excluded. E.g., “id,name,protocol”. Specify subfields in brackets and multiple fields in the same object separated by commas. For a full list of available fields, see: https://ibmsecuritydocs.github.io/qradar_api_20.0/20.0–config-event_sources-log_source_management-log_source_groups-GET.html. | Optional |
| id | ID of a specific disconnected log collector. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.LogSourceGroup.Assignable | Boolean | If log sources can be assigned to this group, the condition is set to true; otherwise, the condition is set to false. Log sources cannot be assigned directly to the “Other” group or to the root log source group node. |
| QRadar.LogSourceGroup.ChildGroupIDs | List | The list of IDs of any child log source groups that belong to this group. |
| QRadar.LogSourceGroup.Description | String | The description of the group. |
| QRadar.LogSourceGroup.ID | Number | The ID of the group. |
| QRadar.LogSourceGroup.ModificationDate | Number | The date and time (expressed as milliseconds since epoch) that the group was last modified. |
| QRadar.LogSourceGroup.Name | String | The name of the group. |
| QRadar.LogSourceGroup.Owner | String | The name of the user who owns the group. |
| QRadar.LogSourceGroup.ParentID | Number | The ID of the group’s parent group. The root node group has a null parent_ID. |
qradar-event-collectors-list
Retrieves a list of event collectors.
Base Command
qradar-event-collectors-list
Input
| Argument Name | Description | Required |
|---|---|---|
| range | Range of results to return (e.g.: 0-20, 3-5, 3-3). Default is 0-49. | Optional |
| filter | Query by which to filter event collectors, e.g., “auto_discovered=false”. For reference, see: https://www.ibm.com/support/knowledgecenter/SS42VS_SHR/com.ibm.qradarapi.doc/c_rest_api_filtering.html. | Optional |
| fields | Comma-separated list of fields to retrieve in the response. Fields that are not explicitly named are excluded. E.g., “id,name,status”. Specify subfields in brackets and multiple fields in the same object separated by commas. For a full list of available fields, see: https://ibmsecuritydocs.github.io/qradar_api_20.0/20.0–config-event_sources-event_collectors-GET.html. | Optional |
| id | ID of a specific event collector. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.EventCollector.Name | String | The display name of the event collector. Not localized because it is derived from a process/component name and the hostname of the managed host it runs on, neither of which are translatable. |
| QRadar.EventCollector.HostID | Number | The ID of the host on which this event collector process runs. |
| QRadar.EventCollector.ComponentName | String | The name of the component backing this event collector process. Also contained in the “name” field. |
| QRadar.EventCollector.ID | Number | The unique ID of the event collector. |
qradar-reference-maps-list
Retrieves a list of reference maps.
Base Command
qradar-reference-maps-list
Input
| Argument Name | Description | Required |
|---|---|---|
| ref_name | The reference name of the reference map for which to retrieve its details. Specify ref_name to get details about a specific reference map. | Optional |
| range | The range of results to return (e.g.: 0-20, 3-5, 3-3). Default is 0-49. | Optional |
| filter | The query by which to filter reference maps, e.g., “timeout_type=FIRST_SEEN”. For reference, see: https://www.ibm.com/support/knowledgecenter/SS42VS_SHR/com.ibm.qradarapi.doc/c_rest_api_filtering.html. When using both ref_name and filter arguments, the filter should be from the data values of the specified reference map, e.g. “value=’1.1.1.1’”. |
Optional |
| fields | A comma-separated list of fields to retrieve in the response. Fields that are not explicitly named are excluded. E.g., “name,timeout_type”. Specify subfields in brackets and multiple fields in the same object separated by commas. For a full list of available fields, see: https://ibmsecuritydocs.github.io/qradar_api_14.0/14.0–reference_data-maps-GET.html. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| QRadar.ReferenceMaps.CollectionID | Number | The collection ID of the reference map. |
| QRadar.ReferenceMaps.NumberOfElements | Number | The number of elements in the reference map. |
| QRadar.ReferenceMaps.TimeToLive | String | The time left to live for the reference. |
| QRadar.ReferenceMaps.Data.LastSeen | Date | The date when this data was last seen in ISO 8601 format (e.g., 2024-01-15T12:34:56Z). |
| QRadar.ReferenceMaps.Data.FirstSeen | Date | The date when this data was first seen in ISO 8601 format (e.g., 2024-01-15T12:34:56Z). |
| QRadar.ReferenceMaps.Data.Source | String | The source of this data. |
| QRadar.ReferenceMaps.Data.Value | String | The data map entry value. |
| QRadar.ReferenceMaps.Data.Key | String | The data map entry key. |
| QRadar.ReferenceMaps.CreationTime | Date | The date when the reference map was created in ISO 8601 format (e.g., 2024-01-15T12:34:56Z). |
| QRadar.ReferenceMaps.Name | String | The name of the reference map. |
| QRadar.ReferenceMaps.Namespace | String | The namespace of the reference map. Possible values: “PRIVATE”, “SHARED”, “TENANT”. |
| QRadar.ReferenceMaps.ElementType | String | The type of the elements in the reference map. |
Command example
#### Context Example
```json
{
"QRadar": {
"ReferenceMaps": [
{
"CollectionID": 145,
"CreationTime": "2025-06-03T07:15:04.824000+00:00",
"ElementType": "ALN",
"Name": "Monitoring Identifier Groups",
"Namespace": "SHARED",
"NumberOfElements": 6,
"TimeoutType": "LAST_SEEN",
"ValueLabel": "Log Source Group"
},
{
"CollectionID": 136,
"CreationTime": "2025-05-14T15:12:07.932000+00:00",
"ElementType": "NUM",
"Name": "Monitoring Identifier Gaps",
"Namespace": "SHARED",
"NumberOfElements": 3,
"TimeoutType": "UNKNOWN"
}
]
}
}
Human Readable Output
Reference Maps List
Name ElementType Namespace CollectionID TimeoutType NumberOfElements ValueLabel CreationTime TimeToLive Monitoring Identifier Groups ALN SHARED 145 LAST_SEEN 6 Log Source Group 2025-06-03T07:15:04.824000+00:00 Monitoring Identifier Gaps NUM SHARED 136 UNKNOWN 3 2025-05-14T15:12:07.932000+00:00
Command example
!qradar-reference-maps-list ref_name="Monitoring Identifier Gaps"
Context Example
{
"QRadar": {
"ReferenceMaps": {
"CollectionID": 136,
"CreationTime": "2025-05-14T15:12:07.932000+00:00",
"Data": [
{
"FirstSeen": "2026-02-10T13:22:50.372000+00:00",
"Key": "Key1",
"LastSeen": "2026-02-10T13:22:50.372000+00:00",
"Source": "reference data api",
"Value": "1234.0"
},
{
"FirstSeen": "2026-02-10T13:27:57.647000+00:00",
"Key": "Key2",
"LastSeen": "2026-02-10T13:27:57.647000+00:00",
"Source": "reference data api",
"Value": "4567.0"
},
{
"FirstSeen": "2026-08-07T06:29:06.602000+00:00",
"Key": "demoKey",
"LastSeen": "2026-08-11T10:40:15.379000+00:00",
"Source": "reference data api",
"Value": "12345.0"
}
],
"ElementType": "NUM",
"Name": "Monitoring Identifier Gaps",
"Namespace": "SHARED",
"NumberOfElements": 3,
"TimeoutType": "UNKNOWN"
}
}
}
Human Readable Output
Reference Maps List
Name ElementType Namespace CollectionID TimeoutType NumberOfElements CreationTime Monitoring Identifier Gaps NUM SHARED 136 UNKNOWN 3 2025-05-14T15:12:07.932000+00:00 Reference Map Data
FirstSeen Key LastSeen Source Value 2026-02-10T13:22:50.372000+00:00 Key1 2026-02-10T13:22:50.372000+00:00 reference data api 1234.0 2026-02-10T13:27:57.647000+00:00 Key2 2026-02-10T13:27:57.647000+00:00 reference data api 4567.0 2026-08-07T06:29:06.602000+00:00 demoKey 2026-08-11T10:40:15.379000+00:00 reference data api 12345.0
qradar-reference-map-value-upsert
Adds or updates an element in a reference map.
Base Command
qradar-reference-map-value-upsert
Input
| Argument Name | Description | Required |
|---|---|---|
| ref_name | The name of the reference map to add or update an element in. Reference names can be found by the ‘Name’ field in the ‘qradar-reference-maps-list’ command. | Required |
| value | The value to insert or update in the reference map. | Required |
| key | The key to update. | Required |
| source | The indication of where the data originated. Default is reference data api. | Optional |
Context Output
There is no context output for this command.
Command example
!qradar-reference-map-value-upsert ref_name="Monitoring Identifier Gaps" value="12345" key="demoKey"
Human Readable Output
Reference map “Monitoring Identifier Gaps” successfully updated: “demoKey” set to “12345”.
Configuration parameters
server— Server URL (required)credentials— Username (required)api_version— QRadar API Version (required)incident_type— Incident Typefirst_fetch— First fetch timestamp (<number> <time unit>, e.g., 12 hours, 7 days)fetch_mode— Fetch moderetry_events_fetch— Retry events fetchevents_limit— Maximum number of events per incident.offenses_per_fetch— Number of offenses to pull per API call (max 50)query— Query to fetch offenses.enrichment— Incidents Enrichmentlimit_assets— Maximum number of assets to fetchevents_columns— Event fields to return from the events query (WARNING: This parameter is correlated to the incoming mapper and changing the values may adversely affect mapping).mirror_options— Mirroring Optionsclose_incident— Close Mirrored XSOAR Incidentmirror_limit— The number of incoming incidents to mirror each timeadv_params— Advanced Parametersinsecure— Trust any certificate (not secure)proxy— Use system proxy settingstimeout— Timeout for http-requestsfetch_interval— Fetch Incidents Interval
Commands (71)
-
get-mapping-fieldsReturns the list of fields for an incident type. This command should be used for debugging purposes.
-
get-modified-remote-dataReturns the list of incidents IDs that were modified since the last update time. Note that this method is for debugging purposes. The get-modified-remote-data command is used as part of the mirroring feature, which is available from version 6.1.
-
get-remote-dataGets remote data from a remote incident. This method does not update the current incident, and should be used for debugging purposes.
-
qradar-assets-listRetrieves assets list.
-
qradar-closing-reasonsRetrieves a list of offense closing reasons.
-
qradar-create-noteDeprecatedCreate a note on an offense. Deprecated, Use qradar-offense-note-create instead.
-
qradar-create-reference-setDeprecatedCreates a new reference set. If the provided name is already in use, this command will fail. Deprecated, Use qradar-reference-set-create instead.
-
qradar-create-reference-set-valueDeprecatedAdd or update a value in a reference set. Deprecated, Use qradar-reference-set-value-upsert instead.
-
qradar-delete-reference-setDeprecatedDeletes a reference set corresponding to the name provided. Deprecated, Use qradar-reference-set-delete instead.
-
qradar-delete-reference-set-valueDeprecatedDeletes a value in a reference set. Deprecated, Use qradar-reference-set-value-delete instead.
-
qradar-disconnected-log-collectors-listRetrieves a list of disconnected log collectors.
-
qradar-domains-listGets the list of domains. You must have System Administrator or Security Administrator permissions to call this endpoint if you are trying to retrieve the details of all domains. You can retrieve details of domains that are assigned to your Security Profile without having the System Administrator or Security Administrator permissions. If you do not have the System Administrator or Security Administrator permissions, then for each domain assigned to your security profile you can only view the values for the ID and name fields. All other values return null.
-
qradar-event-collectors-listRetrieves a list of event collectors.
-
qradar-geolocations-for-ipRetrieves the MaxMind GeoIP data for the specified IP address.
-
qradar-get-asset-by-idDeprecatedRetrieves the asset by id. Deprecated, Use qradar-assets-list instead.
-
qradar-get-assetsDeprecatedList all assets found in the model. Deprecated, Use qradar-assets-list instead.
-
qradar-get-closing-reasonsDeprecatedGet closing reasons. Deprecated, Use qradar-closing-reasons instead.
-
qradar-get-custom-propertiesRetrieves a list of event regex properties.
-
qradar-get-domain-by-idDeprecatedRetrieves Domain information By ID. Deprecated, Use qradar-domains-list instead.
-
qradar-get-domainsDeprecatedRetrieve all Domains. Deprecated, Use qradar-domains-list instead.
-
qradar-get-noteDeprecatedRetrieve a note for an offense. Deprecated, Use qradar-offense-notes-list instead.
-
qradar-get-reference-by-nameDeprecatedInformation about the reference set that had data added or updated. This returns the information set, but not the contained data. This feature is supported from version 8.1 and upward. Deprecated, Use qradar-reference-sets-list instead.
-
qradar-get-searchDeprecatedGets a specific search ID and status. Deprecated, Use qradar-search-status-get instead.
-
qradar-get-search-resultsDeprecatedGets search results. Deprecated, Use qradar-search-results-get instead.
-
qradar-indicators-uploadUploads indicators to QRadar.
-
qradar-ips-local-destination-getGet Source IPs.
-
qradar-ips-source-getGet Source IPs.
-
qradar-log-source-createCreates a new log source.
-
qradar-log-source-deleteDeletes a log source by ID or name. One of the arguments must be provided.
-
qradar-log-source-extensions-listRetrieves a list of log source extensions.
-
qradar-log-source-groups-listRetrieves a list of log source languages.
-
qradar-log-source-languages-listRetrieves a list of log source languages.
-
qradar-log-source-protocol-types-listRetrieves the list of protocol types.
-
qradar-log-source-types-listRetrieves a list of log sources types.
-
qradar-log-source-updateUpdates an exising log source.
-
qradar-log-sources-listRetrieves a list of log sources.
-
qradar-offense-by-idDeprecatedGets offense with matching offense ID from qradar.Deprecated, Use qradar-offense-list instead.
-
qradar-offense-note-createCreates a note on an offense.
-
qradar-offense-notes-listRetrieves a list of notes for an offense.
-
qradar-offense-updateUpdates an offense.
-
qradar-offensesDeprecatedGets offenses from QRadar. Deprecated, Use qradar-offenses-list instead.
-
qradar-offenses-listGets offenses from QRadar.
-
qradar-print-contextReturns a redacted snapshot of integration context metrics (queued, in-progress, finished, meta sizes, concurrent active, samples count). This command is intended for debugging purposes and may return sensitive data.
-
qradar-reference-map-value-upsertAdds or updates an element in a reference map.
-
qradar-reference-maps-listRetrieves a list of reference maps.
-
qradar-reference-set-createCreates a new reference set.
-
qradar-reference-set-deleteRemoves a reference set or purges its contents.
-
qradar-reference-set-value-deleteRemoves a value from a reference set.
-
qradar-reference-set-value-upsertAdds or updates an element in a reference set.
-
qradar-reference-sets-listRetrieves a list of reference sets.
-
qradar-remote-network-cidr-createCreate remote network CIDRs.
-
qradar-remote-network-cidr-deleteDeletes an existing staged remote network.
-
qradar-remote-network-cidr-listRetrieves the list of staged remote networks.
-
qradar-remote-network-cidr-updateUpdates an existing staged remote network.
-
qradar-remote-network-deploy-executionExecutes a deployment. Potentially harmful: This API command executes any waiting system deployments in QRadar within the same deployment type and hosts defined.
-
qradar-reset-last-runResets the fetch incidents last run value, which resets the fetch to its initial fetch state. (Will try to fetch the first available offense).
-
qradar-rule-groups-listRetrieves a list of the rule groups.
-
qradar-rules-listRetrieves a list of rules.
-
qradar-saved-searches-listRetrieves a list of Ariel saved searches.
-
qradar-search-cancelCancels the search in QRadar.
-
qradar-search-createCreates a new asynchronous Ariel search. Returns the search ID. Search status and results can be polled by sending the search ID to the 'qradar-search-status-get' and 'qradar-search-results-get' commands. Accepts SELECT query expressions only.
-
qradar-search-deleteDeletes the search from Qradar.
-
qradar-search-results-getRetrieves search results.
-
qradar-search-retrieve-eventsPolling command to search for events of a specific offense.
-
qradar-search-status-getRetrieves status information for a search, based on the search ID.
-
qradar-searchesDeprecatedSearches in QRadar using AQL. It is highly recommended to use the playbook 'QRadarFullSearch' instead of this command - it will execute the search, and will return the result. Deprecated, Use qradar-search-create instead.
-
qradar-searches-listRetrieves the list of Ariel searches IDs. Search status and results can be polled by sending the search ID to the 'qradar-search-status-get' and 'qradar-search-results-get' commands.
-
qradar-update-offenseDeprecatedUpdate an offense. Deprecated, Use qradar-offense-update instead.
-
qradar-update-reference-set-valueDeprecatedAdds or updates a value in a reference set. Deprecated, Use qradar-reference-set-value-upsert instead.
-
qradar-upload-indicatorsUploads indicators from Demisto to QRadar.
-
qradar-wincollect-destinations-listRetrieves a list of WinCollect destinations. In order to get wincollect_internal_destination_ids - filter internal=true needs to be used In order to get wincollect_external_destination_ids - filter internal=false needs to be used.
import concurrent.futures import copy import secrets import uuid from enum import Enum from ipaddress import ip_address from urllib import parse from collections.abc import Callable from deepmerge import always_merger import pytz import urllib3 from CommonServerPython import * from CommonServerUserPython import * # noqa # Disable insecure warnings urllib3.disable_warnings() # pylint: disable=no-member """ ADVANCED GLOBAL PARAMETERS """ FAILURE_SLEEP = 20 # sleep between consecutive failures events fetch FETCH_SLEEP = arg_to_number(demisto.params().get("fetch_interval")) or 60 # sleep between fetches FETCH_INITIAL_SLEEP = 1 # sleep before the initial check to see if a query has completed. BATCH_SIZE = 100 # batch size used for offense ip enrichment OFF_ENRCH_LIMIT = BATCH_SIZE * 10 # max amount of IPs to enrich per offense MAX_WORKERS = 8 # max concurrent workers used for events enriching DOMAIN_ENRCH_FLG = "true" # when set to true, will try to enrich offense and assets with domain names RULES_ENRCH_FLG = "true" # when set to true, will try to enrich offense with rule names SLEEP_FETCH_EVENT_RETRIES = 10 # sleep between iteration to try search the events of an offense MAX_NUMBER_OF_OFFENSES_TO_CHECK_SEARCH = 5 # Number of offenses to check during mirroring if search was completed. DEFAULT_EVENTS_TIMEOUT = 30 # default timeout for the events enrichment in minutes PROFILING_DUMP_ROWS_LIMIT = 20 MAX_RETRIES_CONTEXT = 5 # max number of retries to update the context MAX_SEARCHES_QUEUE = 10 # maximum number of concurrent searches in mirroring SAMPLE_SIZE = 2 # number of samples to store in integration context MAX_SAMPLE_SIZE_MB = 3 # maximum size in MB for incidents to be stored as samples MAX_SAMPLE_SIZE_BYTES = MAX_SAMPLE_SIZE_MB * 1024 * 1024 # convert MB to bytes EVENTS_INTERVAL_SECS = 60 # interval between events polling EVENTS_MODIFIED_SECS = 5 # interval between events status polling in modified EVENTS_SEARCH_TRIES = 3 # number of retries for creating a new search EVENTS_POLLING_TRIES = 10 # number of retries for events polling EVENTS_SEARCH_RETRY_SECONDS = 100 # seconds between retries to create a new search CONNECTION_ERRORS_RETRIES = 5 # num of times to retry in case of connection-errors CONNECTION_ERRORS_INTERVAL = 1 # num of seconds between each time to send an http-request in case of a connection error. LAST_FETCHED_ID = None ADVANCED_PARAMETERS_STRING_NAMES = [ "DOMAIN_ENRCH_FLG", "RULES_ENRCH_FLG", ] ADVANCED_PARAMETER_INT_NAMES = [ "EVENTS_INTERVAL_SECS", "MAX_SEARCHES_QUEUE", "EVENTS_SEARCH_TRIES", "EVENTS_POLLING_TRIES", "EVENTS_SEARCH_RETRY_SECONDS", "FAILURE_SLEEP", "FETCH_SLEEP", "BATCH_SIZE", "OFF_ENRCH_LIMIT", "MAX_WORKERS", "SLEEP_FETCH_EVENT_RETRIES", "DEFAULT_EVENTS_TIMEOUT", "PROFILING_DUMP_ROWS_LIMIT", ] """ CONSTANTS """ API_USERNAME = "_api_token_key" RESET_KEY = "reset" LAST_FETCH_KEY = "id" MINIMUM_API_VERSION = 10.1 DEFAULT_RANGE_VALUE = "0-49" DEFAULT_TIMEOUT_VALUE = "35" DEFAULT_LIMIT_VALUE = 50 MAXIMUM_MIRROR_LIMIT = 100 DEFAULT_EVENTS_LIMIT = 20 MAXIMUM_OFFENSES_PER_FETCH = 50 DEFAULT_OFFENSES_PER_FETCH = 20 DEFAULT_MIRRORING_DIRECTION = "No Mirroring" MIRROR_OFFENSE_AND_EVENTS = "Mirror Offense and Events" MIRROR_DIRECTION: dict[str, Optional[str]] = {"No Mirroring": None, "Mirror Offense": "In", MIRROR_OFFENSE_AND_EVENTS: "In"} MIRRORED_OFFENSES_QUERIED_CTX_KEY = "mirrored_offenses_queried" MIRRORED_OFFENSES_FINISHED_CTX_KEY = "mirrored_offenses_finished" MIRRORED_OFFENSES_FETCHED_CTX_KEY = "mirrored_offenses_fetched" SAMPLE_INCIDENTS_KEY = "samples" LAST_MIRROR_KEY = "last_mirror_update" LAST_MIRROR_CLOSED_KEY = "last_mirror_closed_update" UTC_TIMEZONE = pytz.timezone("utc") ID_QUERY_REGEX = re.compile(r"(?:\s+|^)id((\s)*)>(=?)((\s)*)((\d)+)(?:\s+|$)") NAME_AND_GROUP_REGEX = re.compile(r"^[\w-]+$") ASCENDING_ID_ORDER = "+id" EXECUTOR = concurrent.futures.ThreadPoolExecutor(max_workers=MAX_WORKERS) DEFAULT_EVENTS_COLUMNS = """QIDNAME(qid), LOGSOURCENAME(logsourceid), CATEGORYNAME(highlevelcategory), CATEGORYNAME(category), PROTOCOLNAME(protocolid), sourceip, sourceport, destinationip, destinationport, QIDDESCRIPTION(qid), username, PROTOCOLNAME(protocolid), RULENAME("creEventList"), sourcegeographiclocation, sourceMAC, sourcev6, destinationgeographiclocation, destinationv6, LOGSOURCETYPENAME(devicetype), credibility, severity, magnitude, eventcount, eventDirection, postNatDestinationIP, postNatDestinationPort, postNatSourceIP, postNatSourcePort, preNatDestinationPort, preNatSourceIP, preNatSourcePort, UTF8(payload), starttime, devicetime""" # noqa: E501 DEFAULT_ASSETS_LIMIT = 100 """ OUTPUT FIELDS REPLACEMENT MAPS """ OFFENSE_OLD_NEW_NAMES_MAP = { "credibility": "Credibility", "relevance": "Relevance", "severity": "Severity", "assigned_to": "AssignedTo", "destination_networks": "DestinationHostname", "status": "Status", "closing_user": "ClosingUser", "closing_reason_id": "ClosingReason", "close_time": "CloseTime", "categories": "Categories", "follow_up": "Followup", "id": "ID", "description": "Description", "source_address_ids": "SourceAddress", "local_destination_address_ids": "DestinationAddress", "remote_destination_count": "RemoteDestinationCount", "start_time": "StartTime", "event_count": "EventCount", "flow_count": "FlowCount", "offense_source": "OffenseSource", "magnitude": "Magnitude", "last_updated_time": "LastUpdatedTime", "offense_type": "OffenseType", "protected": "Protected", "LinkToOffense": "LinkToOffense", "rules": "Rules", "domain_name": "DomainName", "assets": "Assets", } CLOSING_REASONS_RAW_FORMATTED = {"id": "ID", "text": "Name", "is_reserved": "IsReserved", "is_deleted": "IsDeleted"} NOTES_RAW_FORMATTED = {"id": "ID", "note_text": "Text", "create_time": "CreateTime", "username": "CreatedBy"} RULES_RAW_FORMATTED = { "owner": "Owner", "base_host_id": "BaseHostID", "capacity_timestamp": "CapacityTimestamp", "origin": "Origin", "creation_date": "CreationDate", "type": "Type", "enabled": "Enabled", "modification_date": "ModificationDate", "name": "Name", "average_capacity": "AverageCapacity", "id": "ID", "base_capacity": "BaseCapacity", } RULES_GROUP_RAW_FORMATTED = { "owner": "Owner", "modified_time": "ModifiedTime", "level": "Level", "name": "Name", "description": "Description", "id": "ID", "child_groups": "ChildGroups", "child_items": "ChildItems", "type": "Type", "parent_id": "ParentID", } ASSET_RAW_FORMATTED = { "vulnerability_count": "VulnerabilityCount", "interfaces": "Interfaces", "risk_score_sum": "RiskScoreSum", "hostnames": "Hostnames", "id": "ID", "users": "Users", "domain_id": "DomainID", "properties": "Properties", "products": "Products", } SEARCH_RAW_FORMATTED = {"search_id": "ID", "status": "Status"} REFERENCE_SETS_RAW_FORMATTED = { "number_of_elements": "NumberOfElements", "name": "Name", "creation_time": "CreationTime", "element_type": "ElementType", "time_to_live": "TimeToLive", "timeout_type": "TimeoutType", "data": "Data", } REFERENCE_SET_DATA_RAW_FORMATTED = {"last_seen": "LastSeen", "source": "Source", "value": "Value", "first_seen": "FirstSeen"} REFERENCE_MAPS_RAW_FORMATTED = { "number_of_elements": "NumberOfElements", "name": "Name", "creation_time": "CreationTime", "element_type": "ElementType", "namespace": "Namespace", "collection_id": "CollectionID", "data": "Data", "key_label": "KeyLabel", "value_label": "ValueLabel", "time_to_live": "TimeToLive", "timeout_type": "TimeoutType", } REFERENCE_MAP_DATA_RAW_FORMATTED = { "last_seen": "LastSeen", "source": "Source", "value": "Value", "first_seen": "FirstSeen", "key": "Key", } DOMAIN_RAW_FORMATTED = { "asset_scanner_ids": "AssetScannerIDs", "custom_properties": "CustomProperties", "deleted": "Deleted", "description": "Description", "event_collector_ids": "EventCollectorIDs", "flow_collector_ids": "FlowCollectorIDs", "flow_source_ids": "FlowSourceIDs", "id": "ID", "log_source_ids": "LogSourceIDs", "log_source_group_ids": "LogSourceGroupIDs", "name": "Name", "qvm_scanner_ids": "QVMScannerIDs", "tenant_id": "TenantID", } SAVED_SEARCH_RAW_FORMATTED = { "owner": "Owner", "description": "Description", "creation_date": "CreationDate", "uid": "UID", "database": "Database", "is_quick_search": "QuickSearch", "name": "Name", "modified_date": "ModifiedDate", "id": "ID", "aql": "AQL", "is_shared": "IsShared", } IP_GEOLOCATION_RAW_FORMATTED = { "continent": "Continent", "traits": "Traits", "geo_json": "Geolocation", "city": "City", "ip_address": "IPAddress", "represented_country": "RepresentedCountry", "registered_country": "RegisteredCountry", "is_local": "IsLocalCountry", "location": "Location", "postal": "Postal", "physical_country": "PhysicalCountry", "subdivisions": "SubDivisions", } LOG_SOURCES_RAW_FORMATTED = { "sending_ip": "SendingIP", "internal": "Internal", "protocol_parameters": "ProtocolParameters", "description": "Description", "enabled": "Enabled", "group_ids": "GroupIDs", "credibility": "Credibility", "id": "ID", "protocol_type_id": "ProtocolTypeID", "creation_date": "CreationDate", "name": "Name", "modified_date": "ModifiedDate", "auto_discovered": "AutoDiscovered", "type_id": "TypeID", "last_event_time": "LastEventTime", "gateway": "Gateway", "status": "Status", "target_event_collector_id": "TargetEventCollectorID", } TIME_FIELDS_PLACE_HOLDER = 9223372036854775807 # represents the max val that can be stored in a 64-bit signed integer data type. USECS_ENTRIES = { "last_persisted_time", "start_time", "close_time", "create_time", "creation_time", "creation_date", "last_updated_time", "first_persisted_time", "modification_date", "last_seen", "first_seen", "starttime", "devicetime", "last_reported", "created", "last_seen_profiler", "last_seen_scanner", "first_seen_scanner", "first_seen_profiler", "modified_time", "last_event_time", "modified_date", "first_event_flow_seen", "last_event_flow_seen", } LOCAL_DESTINATION_IPS_RAW_FORMATTED = { "domain_id": "DomainID", "event_flow_count": "EventFlowCount", "first_event_flow_seen": "FirstEventFlowSeen", "id": "ID", "last_event_flow_seen": "LastEventFlowSeen", "local_destination_ip": "LocalDestinationIP", "magnitude": "Magnitude", "network": "Network", "offense_ids": "OffenseIDs", "source_address_ids": "SourceAddressIDs", } SOURCE_IPS_RAW_FORMATTED = { "domain_id": "DomainID", "event_flow_count": "EventFlowCount", "first_event_flow_seen": "FirstEventFlowSeen", "id": "ID", "last_event_flow_seen": "LastEventFlowSeen", "local_destination_address_ids": "LocalDestinationAddressIDs", "magnitude": "Magnitude", "network": "Network", "offense_ids": "OffenseIDs", "source_ip": "SourceIP", } EVENT_COLLECTOR_RAW_FORMATTED = {"component_name": "ComponentName", "host_id": "HostID", "id": "ID", "name": "Name"} WINCOLLECT_DESTINATION_RAW_FORMATTED = { "id": "ID", "name": "Name", "host": "Host", "tls_certificate": "TlsCertificate", "port": "Port", "transport_protocol": "TransportProtocol", "inernal": "IsInternal", "event_rate_throttle": "EventRateThrottle", } DISCONNECTED_LOG_COLLECTOR_RAW_FORMATTED = { "id": "ID", "name": "Name", "description": "Description", "protocol": "Protocol", "uuid": "UUID", "version": "Version", } LOG_SOURCE_TYPES_RAW_FORMATTED = { "id": "ID", "name": "Name", "custom": "Custom", "version": "Version", "uuid": "UUID", "supported_language_ids": "SupportedLanguageIDs", "protocol_types": "ProtocolTypes", "default_protocol_id": "DefaultProtocolID", "internal": "Internal", "latest_version": "LatestVersion", "log_source_extension_id": "LogSourceExtensionID", } LOG_SOURCE_PROTOCOL_TYPE_RAW_FORMATTED = { "id": "ID", "name": "Name", "version": "Version", "latest_version": "LatestVersion", "gateway_supported": "GatewaySupported", "inbound": "Inbound", "parameters": "Parameters", "parameter_groups": "ParameterGroups", "testing_capabilities": "TestingCapabilities", } LOG_SOURCE_EXTENSION_RAW_FORMATTED = { "id": "ID", "name": "Name", "description": "Description", "uuid": "UUID", } LOG_SOURCE_LANGUAGE_RAW_FORMATTED = {"id": "ID", "name": "Name"} LOG_SOURCE_GROUP_RAW_FORMATTED = { "id": "ID", "name": "GroupName", "description": "Description", "parent_id": "ParentID", "assignable": "Assignable", } """ ENRICHMENT MAPS """ ASSET_PROPERTIES_NAME_MAP = { "Unified Name": "Name", "CVSS Collateral Damage Potential": "AggregatedCVSSScore", "Weight": "Weight", } FULL_ASSET_PROPERTIES_NAMES_MAP = { "Compliance Notes": "ComplianceNotes", "Compliance Plan": "CompliancePlan", "Location": "Location", "Switch ID": "SwitchID", "Switch Port ID": "SwitchPort", "Group Name": "GroupName", "Vulnerabilities": "Vulnerabilities", } LONG_RUNNING_REQUIRED_PARAMS = { "fetch_mode": "Fetch mode", "offenses_per_fetch": "Number of offenses to pull per API call (max 50)", "events_limit": "Maximum number of events per incident.", } """ ENUMS """ class FetchMode(str, Enum): """ Enums for the options of fetching the incidents. """ no_events = "Fetch Without Events" all_events = "Fetch With All Events" correlations_events_only = "Fetch Correlation Events Only" class QueryStatus(str, Enum): """ Enums for the options of fetching the events. """ WAIT = "wait" ERROR = "error" SUCCESS = "success" PARTIAL = "partial" @classmethod def values(cls) -> list[str]: """ Gets the values of all enum members. Returns: list[str]: The values of the enum class members. """ return [member.value for member in cls] FIELDS_MIRRORING = "id,start_time,event_count,last_persisted_time,close_time" """ CLIENT CLASS """ class Client(BaseClient): def __init__( self, server: str, verify: bool, proxy: bool, api_version: str, credentials: dict, timeout: int | float | None = None ): username = credentials.get("identifier") password = credentials.get("password") if username == API_USERNAME: auth_type = "API Key" self.base_headers = {"Version": api_version, "SEC": password} auth = None else: auth_type = "Basic" auth = (username, password) self.base_headers = {"Version": api_version} base_url = urljoin(server, "/api") super().__init__(base_url=base_url, verify=verify, proxy=proxy, auth=auth) self.timeout = timeout # type: ignore[assignment] self.password = password self.server = server demisto.debug(f"QRadar client instance initialized with: {server=}, {verify=}, {proxy=}, {api_version=}, {auth_type=}.") def http_request( self, method: str, url_suffix: str, params: Optional[dict] = None, json_data: Optional[dict | list[dict]] = None, data: Optional[dict] = None, additional_headers: Optional[dict] = None, timeout: Optional[int] = None, resp_type: str = "json", ) -> Any: headers = {**additional_headers, **self.base_headers} if additional_headers else self.base_headers for _time in range(1, CONNECTION_ERRORS_RETRIES + 1): try: return self._http_request( method=method, url_suffix=url_suffix, params=params, json_data=json_data, data=data, headers=headers, error_handler=self.qradar_error_handler, timeout=timeout or self.timeout, resp_type=resp_type, with_metrics=True, ) except (DemistoException, requests.ReadTimeout) as error: demisto.error(f"Error {error} in time {_time}") if ( isinstance(error, DemistoException) and not isinstance(error.exception, requests.ConnectionError) ) or _time == CONNECTION_ERRORS_RETRIES: raise else: time.sleep(CONNECTION_ERRORS_INTERVAL) # pylint: disable=sleep-exists return None @staticmethod def qradar_error_handler(res: requests.Response): """ QRadar error handler for any error occurred during the API request. This function job is to translate the known exceptions returned by QRadar to human readable exception to help the user understand why the request have failed. If error returned is not in the expected error format, raises the exception as is. Args: res (Any): The error response returned by QRadar. Returns: - raises DemistoException. """ err_msg = f"Error in API call [{res.status_code}] - {res.reason}" try: # Try to parse json error response error_entry = res.json() message = error_entry.get("message", "") if "items=x-y" in message: message = "Failed to parse Range argument. The syntax of the Range argument must follow this pattern: x-y" elif "unauthorized to access" in err_msg or "No SEC header present in request" in err_msg: message = "Authorization Error: make sure credentials are correct." elif "The specified encryption strength is not available" in err_msg: err_msg = "" message = "The specified encryption is not available, try using a weaker encryption (AES128)." elif "User has insufficient capabilities to access this endpoint resource" in message: message = ( "The given credentials do not have the needed permissions to perform the call the endpoint" f"\n{res.request.path_url}.\n" "Please supply credentials with the needed permissions as can be seen in the integration " "description, or do not call or enrich offenses with the mentioned endpoint." ) err_msg += f"\n{message}" raise DemistoException(err_msg, res=res) except ValueError as e: err_msg += f"\n{res.text}" raise DemistoException(err_msg, res=res) from e def offenses_list( self, range_: Optional[str] = None, offense_id: Optional[int] = None, filter_: Optional[str] = None, fields: Optional[str] = None, sort: Optional[str] = None, ): id_suffix = f"/{offense_id}" if offense_id else "" params = assign_params(fields=fields) if offense_id else assign_params(filter=filter_, fields=fields, sort=sort) additional_headers = {"Range": range_} if not offense_id else None return self.http_request( method="GET", url_suffix=f"/siem/offenses{id_suffix}", params=params, additional_headers=additional_headers ) def offense_update( self, offense_id: int, protected: Optional[str] = None, follow_up: Optional[str] = None, status: Optional[str] = None, closing_reason_id: Optional[int] = None, assigned_to: Optional[str] = None, fields: Optional[str] = None, ): return self.http_request( method="POST", url_suffix=f"/siem/offenses/{offense_id}", params=assign_params( protected=protected, follow_up=follow_up, status=status, closing_reason_id=closing_reason_id, assigned_to=assigned_to, fields=fields, ), ) def closing_reasons_list( self, closing_reason_id: Optional[int] = None, include_reserved: Optional[bool] = None, include_deleted: Optional[bool] = None, range_: Optional[str] = None, filter_: Optional[str] = None, fields: Optional[str] = None, ): id_suffix = f"/{closing_reason_id}" if closing_reason_id else "" params = ( assign_params(fields=fields) if closing_reason_id else assign_params(include_reserved=include_reserved, include_deleted=include_deleted, filter=filter_, fields=fields) ) additional_headers = {"Range": range_} if not closing_reason_id and range_ else None return self.http_request( method="GET", url_suffix=f"/siem/offense_closing_reasons{id_suffix}", additional_headers=additional_headers, params=params, ) def offense_notes_list( self, offense_id: int, range_: str, note_id: Optional[int] = None, filter_: Optional[str] = None, fields: Optional[str] = None, ): note_id_suffix = f"/{note_id}" if note_id else "" params = assign_params(fields=fields) if note_id else assign_params(filter=filter_, fields=fields) additional_headers = {"Range": range_} if not note_id else None return self.http_request( method="GET", url_suffix=f"/siem/offenses/{offense_id}/notes{note_id_suffix}", additional_headers=additional_headers, params=params, ) def offense_notes_create(self, offense_id: int, note_text: str, fields: Optional[str] = None): return self.http_request( method="POST", url_suffix=f"/siem/offenses/{offense_id}/notes", params=assign_params(note_text=note_text, fields=fields), ) def rules_list( self, rule_id: Optional[str] = None, range_: Optional[str] = None, filter_: Optional[str] = None, fields: Optional[str] = None, ): id_suffix = f"/{rule_id}" if rule_id else "" params = assign_params(fields=fields) if rule_id else assign_params(filter=filter_, fields=fields) additional_headers = {"Range": range_} if range_ and not rule_id else None return self.http_request( method="GET", url_suffix=f"/analytics/rules{id_suffix}", params=params, additional_headers=additional_headers ) def rule_groups_list( self, range_: str, rule_group_id: Optional[int] = None, filter_: Optional[str] = None, fields: Optional[str] = None ): id_suffix = f"/{rule_group_id}" if rule_group_id else "" additional_headers = {"Range": range_} if not rule_group_id else None params = assign_params(fields=fields) if rule_group_id else assign_params(filter=filter_, fields=fields) return self.http_request( method="GET", url_suffix=f"/analytics/rule_groups{id_suffix}", additional_headers=additional_headers, params=params ) def assets_list(self, range_: Optional[str] = None, filter_: Optional[str] = None, fields: Optional[str] = None): return self.http_request( method="GET", url_suffix="/asset_model/assets", additional_headers={"Range": range_}, params=assign_params(filter=filter_, fields=fields), ) def saved_searches_list( self, range_: str, timeout: Optional[int], saved_search_id: Optional[str] = None, filter_: Optional[str] = None, fields: Optional[str] = None, ): id_suffix = f"/{saved_search_id}" if saved_search_id else "" params = assign_params(fields=fields) if saved_search_id else assign_params(filter=filter_, fields=fields) additional_headers = {"Range": range_} if not saved_search_id else None return self.http_request( method="GET", url_suffix=f"/ariel/saved_searches{id_suffix}", additional_headers=additional_headers, params=params, timeout=timeout, ) def searches_list(self, range_: str, filter_: Optional[str] = None): return self.http_request( method="GET", url_suffix="/ariel/searches", additional_headers={"Range": range_}, params=assign_params(filter=filter_) ) def search_create(self, query_expression: Optional[str] = None, saved_search_id: Optional[str] = None): return self.http_request( method="POST", url_suffix="/ariel/searches", params=assign_params(query_expression=query_expression, saved_search_id=saved_search_id), ) def search_status_get(self, search_id: str): return self.http_request( method="GET", url_suffix=f"/ariel/searches/{search_id}", ) def search_delete(self, search_id: str): return self.http_request( method="DELETE", url_suffix=f"/ariel/searches/{search_id}", ) def search_cancel(self, search_id: str): return self.http_request( method="POST", url_suffix=f"/ariel/searches/{search_id}?status=CANCELED", ) def search_results_get(self, search_id: str, range_: Optional[str] = None): return self.http_request( method="GET", url_suffix=f"/ariel/searches/{search_id}/results", additional_headers={"Range": range_} if range_ else None, ) def reference_sets_list( self, range_: Optional[str] = None, ref_name: Optional[str] = None, filter_: Optional[str] = None, fields: Optional[str] = None, ): name_suffix = f"/{parse.quote(ref_name, safe='')}" if ref_name else "" params = assign_params(filter=filter_, fields=fields) additional_headers = {"Range": range_} return self.http_request( method="GET", url_suffix=f"/reference_data/sets{name_suffix}", params=params, additional_headers=additional_headers ) def reference_maps_list( self, range_: Optional[str] = None, ref_name: Optional[str] = None, filter_: Optional[str] = None, fields: Optional[str] = None, ): name_suffix = f"/{parse.quote(ref_name, safe='')}" if ref_name else "" params = assign_params(filter=filter_, fields=fields) additional_headers = {"Range": range_} return self.http_request( method="GET", url_suffix=f"/reference_data/maps{name_suffix}", params=params, additional_headers=additional_headers ) def reference_set_create( self, ref_name: str, element_type: str, timeout_type: Optional[str] = None, time_to_live: Optional[str] = None, fields: Optional[str] = None, ): return self.http_request( method="POST", url_suffix="/reference_data/sets", params=assign_params( name=ref_name, element_type=element_type, timeout_type=timeout_type, time_to_live=time_to_live, fields=fields ), ) def reference_set_delete(self, ref_name: str, purge_only: Optional[str] = None, fields: Optional[str] = None): return self.http_request( method="DELETE", url_suffix=f"/reference_data/sets/{parse.quote(parse.quote(ref_name, safe=''), safe='')}", params=assign_params(purge_only=purge_only, fields=fields), ) def reference_set_value_upsert(self, ref_name: str, value: str, source: Optional[str] = None, fields: Optional[str] = None): return self.http_request( method="POST", url_suffix=f"/reference_data/sets/{parse.quote(ref_name, safe='')}", params=assign_params(value=value, source=source, fields=fields), ) def reference_map_value_upsert(self, ref_name: str, key: str, value: str, source: str | None = None): return self.http_request( method="POST", url_suffix=f"/reference_data/maps/{parse.quote(ref_name, safe='')}", params=assign_params(key=key, value=value, source=source), ) def reference_set_value_delete(self, ref_name: str, value: str): double_encoded_value = parse.quote(parse.quote(value, safe=""), safe="") double_encoded_ref_name = parse.quote(parse.quote(ref_name, safe=""), safe="") return self.http_request( method="DELETE", url_suffix=f"/reference_data/sets/{double_encoded_ref_name}/{double_encoded_value}" ) def domains_list( self, domain_id: Optional[int] = None, range_: Optional[str] = None, filter_: Optional[str] = None, fields: Optional[str] = None, ): id_suffix = f"/{domain_id}" if domain_id else "" params = assign_params(fields=fields) if domain_id else assign_params(filter=filter_, fields=fields) additional_headers = {"Range": range_} if not domain_id and range_ else None return self.http_request( method="GET", url_suffix=f"/config/domain_management/domains{id_suffix}", additional_headers=additional_headers, params=params, ) def reference_set_bulk_load(self, ref_name: str, indicators: Any, fields: Optional[str] = None): headers = {"Content-Type": "application/json"} if fields: headers["fields"] = fields return self.http_request( method="POST", url_suffix=f"/reference_data/sets/bulk_load/{parse.quote(ref_name, safe='')}", json_data=indicators, additional_headers=headers, ) def reference_set_entries( self, ref_name: str, indicators: Any, fields: Optional[str] = None, source: Optional[str] = None, timeout: Optional[int] = None, ): headers = {"Content-Type": "application/json"} if fields: headers["fields"] = fields name = parse.quote(ref_name, safe="") sets = self.http_request(method="GET", url_suffix=f"/reference_data/sets/{name}") if not sets: raise DemistoException(f"Reference set {ref_name} does not exist.") set_id = sets.get("collection_id") return self.http_request( method="PATCH", url_suffix="/reference_data_collections/set_entries", json_data=[{"collection_id": set_id, "value": str(indicator), "source": source} for indicator in indicators], # type: ignore[arg-type] additional_headers=headers, timeout=timeout, ) def get_reference_data_bulk_task_status(self, task_id: int): return self.http_request(method="GET", url_suffix=f"/reference_data_collections/set_bulk_update_tasks/{task_id}") def geolocations_for_ip(self, filter_: Optional[str] = None, fields: Optional[str] = None): return self.http_request( method="GET", url_suffix="/services/geolocations", params=assign_params(filter=filter_, fields=fields) ) def log_sources_list( self, qrd_encryption_algorithm: str, qrd_encryption_password: str, range_: str, filter_: Optional[str] = None, fields: Optional[str] = None, ): return self.http_request( method="GET", url_suffix="/config/event_sources/log_source_management/log_sources", params=assign_params(filter=filter_, fields=fields), additional_headers={ "x-qrd-encryption-algorithm": qrd_encryption_algorithm, "x-qrd-encryption-password": qrd_encryption_password, "Range": range_, }, ) def get_log_source(self, qrd_encryption_algorithm: str, qrd_encryption_password: str, id: str, fields: Optional[str] = None): return self.http_request( method="GET", url_suffix=f"/config/event_sources/log_source_management/log_sources/{id}", params=assign_params(fields=fields), additional_headers={ "x-qrd-encryption-algorithm": qrd_encryption_algorithm, "x-qrd-encryption-password": qrd_encryption_password, }, ) def custom_properties(self, range_: Optional[str] = None, filter_: Optional[str] = None, fields: Optional[str] = None): return self.http_request( method="GET", url_suffix="/config/event_sources/custom_properties/regex_properties", params=assign_params(filter=filter_, fields=fields), additional_headers={"Range": range_} if range_ else None, ) def offense_types(self, filter_: Optional[str] = None, fields: Optional[str] = None): return self.http_request( method="GET", url_suffix="/siem/offense_types", params=assign_params(filter=filter_, fields=fields) ) def get_addresses( self, address_suffix: str, filter_: Optional[str] = None, fields: Optional[str] = None, range_: Optional[str] = None ): return self.http_request( method="GET", url_suffix=f"/siem/{address_suffix}", params=assign_params(filter=filter_, fields=fields), additional_headers={"Range": range_} if range_ else None, ) def create_and_update_remote_network_cidr(self, body: dict[str, Any], fields: str, update: bool = False): headers = {"fields": fields} return self.http_request( method="POST", url_suffix="/staged_config/remote_networks" + (f"/{body.get('id')}" if update else ""), json_data=body, additional_headers=headers, ) def get_remote_network_cidr(self, range_: Optional[str] = None, filter_: Optional[str] = None, fields: Optional[str] = None): headers = {"Range": range_} params = assign_params(filter=filter_, fields=fields) return self.http_request( method="GET", url_suffix="/staged_config/remote_networks", params=params, additional_headers=headers ) def delete_remote_network_cidr(self, id_): return self.http_request(method="DELETE", url_suffix=f"/staged_config/remote_networks/{id_}", resp_type="response") def remote_network_deploy_execution(self, body): return self.http_request(method="POST", url_suffix="/staged_config/deploy_status", json_data=body) def get_resource_list( self, range_: str, endpoint: str, filter_: Optional[str] = None, fields: Optional[str] = None, additional_headers_: Optional[dict] = None, ): """ Retrieve a list of resources from a specified endpoint. Args: range_ (str): The range of resources to retrieve. eg. items=0-49 endpoint (str): The API endpoint to retrieve resources from. filter_ (Optional[str], optional): Optional filter query for the request. Defaults to None. fields (Optional[str], optional): The fields that the API should return. Defaults to None. additional_headers_ (Optional[dict], optional): Optional additional headers for the request. Defaults to None. Returns: Response: The response object from the HTTP request. """ return self.http_request( method="GET", url_suffix=endpoint, params=assign_params(filter=filter_, fields=fields), additional_headers={"Range": range_} if additional_headers_ is None else {"Range": range_, **additional_headers_}, ) def get_resource_by_id(self, id: str, endpoint: str, fields: Optional[str] = None, additional_headers: Optional[dict] = None): return self.http_request( method="GET", url_suffix=endpoint + f"/{id}", params=assign_params(fields=fields), additional_headers=additional_headers, ) def get_resource( self, id, range_: str, endpoint: str, filter_: Optional[str] = None, fields: Optional[str] = None, additional_headers_: Optional[dict] = None, ): return ( self.get_resource_list(range_, endpoint, filter_, fields, additional_headers_) if id is None else [self.get_resource_by_id(id, endpoint, fields, additional_headers_)] ) def delete_log_source(self, id: str) -> requests.Response: return self.http_request( method="DELETE", url_suffix=f"/config/event_sources/log_source_management/log_sources/{id}", resp_type="response" ) def create_log_source(self, log_source: dict): return self.http_request( method="POST", url_suffix="/config/event_sources/log_source_management/log_sources", json_data=log_source ) def update_log_source(self, log_source: dict[str, Any]): return self.http_request( method="PATCH", url_suffix="/config/event_sources/log_source_management/log_sources", json_data=[log_source], resp_type="response", ) def test_connection(self): """ Test connection with databases (should always be up) """ self.http_request(method="GET", url_suffix="/ariel/databases") return "ok" """ HELPER FUNCTIONS """ def get_major_version(version: str) -> int: try: if "." not in version: return int(version) return int(version.split(".")[0]) except ValueError: print_debug_msg(f"Could not parse version {version} to int") return 17 def insert_values_to_reference_set_polling( client: Client, api_version: str, args: dict, from_indicators: bool = False, values: list[str] | None = None, ) -> PollResult: """This function inserts values to reference set using polling method. Args: client (Client): QRadar Client api_version (str): The API version of QRadar. args (dict): args of the command from_indicators (bool, optional): Whether to insert values from XSOAR indicators. Defaults to False. values (list[str] | None, optional): The values to insert. Defaults to None. Raises: DemistoException: If there are no values to insert Returns: PollResult: The result with the CommandResults """ response = {} use_old_api = get_major_version(api_version) <= 15 ref_name = args.get("ref_name", "") try: if use_old_api or "task_id" not in args: if from_indicators: query = args.get("query") limit = arg_to_number(args.get("limit", DEFAULT_LIMIT_VALUE)) page = arg_to_number(args.get("page", 0)) # Backward compatibility for QRadar V2 command. Create reference set for given 'ref_name' if does not exist. element_type = args.get("element_type", "") timeout_type = args.get("timeout_type") time_to_live = args.get("time_to_live") try: client.reference_sets_list(ref_name=ref_name) except DemistoException as e: # Create reference set if does not exist if e.message and f"{ref_name} does not exist" in e.message: # if this call fails, raise an error and stop command execution client.reference_set_create(ref_name, element_type, timeout_type, time_to_live) else: raise e search_indicators = IndicatorsSearcher(page=page) indicators = search_indicators.search_indicators_by_version(query=query, size=limit).get("iocs", []) indicators_data = [ {"Indicator Value": indicator.get("value"), "Indicator Type": indicator.get("indicator_type")} for indicator in indicators if "value" in indicator and "indicator_type" in indicator ] values = [indicator.get("Indicator Value", "") for indicator in indicators_data] if not indicators_data: return PollResult( CommandResults( readable_output=f"No indicators were found for reference set {ref_name}", ) ) if not values: raise DemistoException("Value to insert must be given.") source = args.get("source") date_value = argToBoolean(args.get("date_value", False)) fields = args.get("fields") if date_value: values = [get_time_parameter(value, epoch_format=True) for value in values] if use_old_api: response = client.reference_set_bulk_load(ref_name, values, fields) else: response = client.reference_set_entries(ref_name, values, fields, source, timeout=300) args["task_id"] = response.get("id") if not use_old_api: response = client.get_reference_data_bulk_task_status(args["task_id"]) except (DemistoException, requests.Timeout) as e: if "task_id" in args: print_debug_msg( f"Polling task status {args['task_id']} failed due to {e}. " f"Will try to poll task status again in the next interval." ) else: print_debug_msg(f"Failed inserting values to reference due to {e}, will retry in the insert in the next interval") response = {} if use_old_api or response.get("status") == "COMPLETED": if not use_old_api: # get the reference set data response = client.reference_sets_list(ref_name=ref_name) key_replace_dict = { k: v for k, v in REFERENCE_SETS_RAW_FORMATTED.items() if k != "data" or not argToBoolean(args.get("quiet_mode") or False) } outputs = sanitize_outputs(response, key_replace_dict) command_results = CommandResults( readable_output=tableToMarkdown("Reference Update Create", outputs, removeNull=True), outputs_prefix="QRadar.Reference", outputs_key_field="Name", outputs=outputs, raw_response=response, ) return PollResult(command_results, continue_to_poll=False) return PollResult( partial_result=CommandResults( readable_output=f"Reference set {ref_name} is still being updated in task {args['task_id']}" ), continue_to_poll=True, args_for_next_run=args, response=None, ) def get_remote_events( client: Client, offense_id: str, context_data: dict, events_columns: str, events_limit: int, fetch_mode: str, ) -> tuple[list[dict], str]: """ Get the remote events of the `offense_id` It will update the context data as well Args: client (Client): QRadar client offense_id (str): Offense id to update context_data (dict): The current context data events_columns (str): events columns of AQL events_limit (int): events limit of AQL fetch_mode (str): The fetch mode configure Returns: Tuple[list[dict], SearchQueryStatus]: List of events of the offense id, the status of the request """ changed_ids_ctx = [] offenses_queried = context_data.get(MIRRORED_OFFENSES_QUERIED_CTX_KEY, {}) offenses_finished = context_data.get(MIRRORED_OFFENSES_FINISHED_CTX_KEY, {}) offenses_fetched = context_data.get(MIRRORED_OFFENSES_FETCHED_CTX_KEY, {}) events: list[dict] = [] status = QueryStatus.ERROR.value if offenses_queried.get(offense_id) == QueryStatus.ERROR.value: return events, QueryStatus.ERROR.value if offense_id not in offenses_finished or offenses_queried.get(offense_id, "") in { QueryStatus.WAIT.value, QueryStatus.ERROR.value, }: # if our offense not in the finished list, we will create a new search # the value will be error because we don't want to wait until the search is complete search_id = create_events_search(client, fetch_mode, events_columns, events_limit, int(offense_id)) offenses_queried[offense_id] = search_id changed_ids_ctx.append(offense_id) elif offense_id in offenses_finished: # if our offense is in finished list, we will get the result search_id = offenses_finished[offense_id] try: search_results = client.search_results_get(search_id) events = search_results.get("events", []) del offenses_finished[offense_id] changed_ids_ctx.append(offense_id) status = QueryStatus.SUCCESS.value except Exception as e: # getting results failed, move back to queried queue to be queried again del offenses_finished[offense_id] changed_ids_ctx.append(offense_id) offenses_queried[offense_id] = QueryStatus.ERROR.value status = QueryStatus.ERROR.value print_debug_msg(f"No results for {offense_id}. Error: {e}. Stopping execution") time.sleep(FAILURE_SLEEP) elif offense_id in offenses_queried: search_id = offenses_queried[offense_id] events, status = poll_offense_events(client, search_id, should_get_events=True, offense_id=int(offense_id)) if status == QueryStatus.SUCCESS.value: del offenses_queried[offense_id] changed_ids_ctx.append(offense_id) if status == QueryStatus.SUCCESS.value: offenses_fetched[offense_id] = get_num_events(events) partial_changes = { MIRRORED_OFFENSES_QUERIED_CTX_KEY: offenses_queried, MIRRORED_OFFENSES_FINISHED_CTX_KEY: offenses_finished, MIRRORED_OFFENSES_FETCHED_CTX_KEY: offenses_fetched, } safely_update_context_data_partial( partial_changes, override_keys=list(partial_changes.keys()), # need to override (not merge!) since offense may have been deleted ) return events, status def update_user_query(user_query: str) -> str: return f" AND ({user_query})" if user_query else "" def merge_samples(current_ctx: dict, changes: dict) -> None: """Merges samples from `changes` into `current_ctx`. Args: current_ctx (dict): The current integration context. changes (dict): The changes to be merged into the integration context. """ new_samples = changes.pop(SAMPLE_INCIDENTS_KEY, []) current_samples = current_ctx.get(SAMPLE_INCIDENTS_KEY, []) if isinstance(current_samples, list): # Ensure samples do not grow unbounded due to the list appending behavior of always_merger # Store samples from the LAST iteration by prioritizing new_samples over current_samples demisto.debug("Storing new samples from the last iteration in context.") current_ctx[SAMPLE_INCIDENTS_KEY] = (new_samples + current_samples)[:SAMPLE_SIZE] else: # If samples is a JSON string (legacy context schema), then override demisto.debug("Setting new samples in context.") current_ctx[SAMPLE_INCIDENTS_KEY] = new_samples[:SAMPLE_SIZE] def remove_context_keys( current_ctx: dict, changes: dict, override_keys: list[str], ): """Removes the values in the `current_ctx` if they exist in `changes` before merging. Args: current_ctx (dict): The current integration context. changes (dict): The changes to be merged into the integration context. override_keys (list[str]): The list of current_ctx keys to override, if value exists. """ demisto.debug(f"Overriding keys in current context: {', '.join(override_keys)}.") removed_keys = set() for key in override_keys: if key in current_ctx and key in changes: current_ctx.pop(key, None) removed_keys.add(key) demisto.debug(f"Removed keys from current integration context for overriding: {', '.join(removed_keys)}.") def deep_merge_context_changes( current_ctx: dict, changes: dict, override_keys: list[str], ) -> None: """Recursively merges `changes` into `current_ctx` using the `deepmerge` package. Args: current_ctx (dict): The current integration context. changes (dict): The changes to be merged into the integration context. override_keys (list[str]): The list of current_ctx keys to override, if value exists. """ if override_keys: # remove values in the `current_ctx` if they exist in `changes` before merging remove_context_keys(current_ctx, changes, override_keys) merge_samples(current_ctx, changes) always_merger.merge(current_ctx, changes) # updates `current_ctx` in place with `changes` def qradar_get_integration_context(): global LAST_FETCHED_ID try: context_data = get_integration_context() except AttributeError as e: demisto.error(f"Failed to retrieve the QRadar integration context because it is not a dictionary: {str(e)}") sys.exit(1) # pylint: disable=E9001 except Exception as e: demisto.error(f"Failed to get QRadar integration context: {str(e)}") sys.exit(1) # pylint: disable=E9001 if context_data and context_data.get(LAST_FETCH_KEY) and LAST_FETCHED_ID: ctx_last_fetch_id = int(context_data[LAST_FETCH_KEY]) if ctx_last_fetch_id < int(LAST_FETCHED_ID): demisto.error(f"invalid QRadar integration context {ctx_last_fetch_id=} {LAST_FETCHED_ID=}") sys.exit(1) # pylint: disable=E9001 return context_data def qradar_set_integration_context(context_data): global LAST_FETCHED_ID set_integration_context(context_data) last_fetch_id = context_data.get(LAST_FETCH_KEY) if last_fetch_id: LAST_FETCHED_ID = last_fetch_id demisto.debug(f"LAST_FETCHED_ID variable set to {LAST_FETCHED_ID}") else: demisto.debug("LAST_FETCH_KEY not found in context") def safely_update_context_data_partial( changes: dict, attempts: int = 5, override_keys: Optional[list[str]] = None, ) -> None: """ Reads the current integration context+version, deep-merges `changes` into it, then writes it back. Retries up to `attempts` times if there's a version conflict. """ override_keys = override_keys or [] changes_size_bytes = {key: calculate_object_size(value) for key, value in changes.items()} print_debug_msg(f"Updating context with {changes=}, {override_keys=}, {changes_size_bytes=}.") for _ in range(attempts): ctx = get_integration_context() merged = copy.deepcopy(ctx) deep_merge_context_changes(merged, changes, override_keys=override_keys) merged_size_bytes = {key: calculate_object_size(value) for key, value in merged.items()} try: print_debug_msg(f"Saving merged context, {merged_size_bytes=}.") qradar_set_integration_context(merged) return # success except Exception as e: print_debug_msg(f"Version conflict or error setting context: {e}. Retrying...") raise DemistoException(f"Failed updating context after {attempts} attempts.") def add_iso_entries_to_dict(dicts: List[dict]) -> List[dict]: """ Takes list of dicts, for each dict: creates a new dict, and for each field in the output that is contained in 'USECS_ENTRIES', maps its value to be iso format corresponding to the value of the field. Args: dicts (List[Dict]): List of the dicts to be transformed. Returns: (List[Dict]): New dicts with iso entries for the corresponding items in 'USECS_ENTRIES' """ return [ {k: (get_time_parameter(v, iso_format=True) if should_get_time_parameter(k, v) else v) for k, v in dict_.items()} for dict_ in dicts ] def should_get_time_parameter(k: str, v: Union[Optional[str], Optional[int]]) -> bool: """Checks whether the given key should be converted or not. The variable should be converted if the key is in the USECS_ENTRIES list and the value is valid. Args: k (str): the key of the field v (Union[Optional[str], Optional[int]]): the field value Returns: bool: True if it should be converted, otherwise return False. """ valid_value = isinstance(v, str) or v != TIME_FIELDS_PLACE_HOLDER return k in USECS_ENTRIES and valid_value def sanitize_outputs(outputs: Any, key_replace_dict: Optional[dict] = None) -> List[dict]: """ Gets a list of all the outputs, and sanitizes outputs. - Removes empty elements. - adds ISO entries to the outputs. - Outputs only keys found in 'key_replace_dict', saving their names by 'key_replace_dict values, if 'key_replace_dict' is not None. Args: outputs (List[Dict]): List of the outputs to be sanitized. key_replace_dict (Dict): Dict of the keys to transform their names. Returns: (List[Dict]): Sanitized outputs. """ if not isinstance(outputs, list): outputs = [outputs] outputs = [remove_empty_elements(output) for output in outputs] outputs = add_iso_entries_to_dict(outputs) return build_final_outputs(outputs, key_replace_dict) if key_replace_dict else outputs def get_time_parameter(arg: Union[Optional[str], Optional[int]], iso_format: bool = False, epoch_format: bool = False): """ parses arg into date time object with aware time zone if 'arg' exists. If no time zone is given, sets timezone to UTC. Returns the date time object created/ISO format/epoch format. Args: arg (str): The argument to turn into aware date time. iso_format (bool): Whether to return date or the parsed format of the date. epoch_format (bool): Whether to return date or the epoch format of the date. Returns: - (None) If 'arg' is None, returns None. - (datetime): If 'arg' is exists and 'iso_format' and 'epoch_format' are false, returns date time. - (str): If 'arg' is exists and parse_format is true, returns ISO format of the date time object. - (int): If 'arg' is exists and epoch_format is true, returns epoch format of the date time object. """ try: maybe_unaware_date = arg_to_datetime(arg, is_utc=True) if not maybe_unaware_date: return None aware_time_date = maybe_unaware_date if maybe_unaware_date.tzinfo else UTC_TIMEZONE.localize(maybe_unaware_date) if iso_format: return aware_time_date.isoformat() if epoch_format: return int(aware_time_date.timestamp() * 1000) return aware_time_date except Exception as e: demisto.info(f"Could not convert time for {arg=}, reason {e}") return arg def build_final_outputs(outputs: List[dict], old_new_dict: dict) -> List[dict]: """ Receives outputs, or a single output, and a dict containing mapping of old key names to new key names. Returns a list of outputs containing the new names contained in old_new_dict. Args: outputs (Dict): Outputs to replace its keys. old_new_dict (Dict): Old key name mapped to new key name. Returns: (Dict): The dictionary with the transformed keys and their values. """ return [{old_new_dict.get(k): v for k, v in output.items() if k in old_new_dict} for output in outputs] def build_headers(first_headers: List[str], all_headers: set[str]) -> List[str]: """ Receives headers to be shown first in entry room, and concat all the headers after first headers. Args: first_headers (Set[str]): First headers to be shown in the entry room. all_headers (Set[str]): List of all of the headers. Returns: (List[str]): List of all of the headers, where first_headers are first in the list. """ return first_headers + list(set.difference(all_headers, first_headers)) def is_valid_ip(ip: str) -> bool: try: ip_address(ip) return True except ValueError: print_debug_msg(f"IP {ip} was found invalid.") return False def get_offense_types(client: Client, offenses: List[dict]) -> dict: """ Receives list of offenses, and performs API call to QRadar service to retrieve the offense type names matching the offense type IDs of the offenses. Args: client (Client): Client to perform the API request to QRadar. offenses (List[Dict]): List of all of the offenses. Returns: (Dict): Dictionary of {offense_type_id: offense_type_name} """ try: offense_types_ids = {offense.get("offense_type") for offense in offenses if offense.get("offense_type") is not None} if not offense_types_ids: return {} offense_types = client.offense_types(filter_=f"""id in ({",".join(map(str, offense_types_ids))})""", fields="id,name") return {offense_type.get("id"): offense_type.get("name") for offense_type in offense_types} except Exception as e: demisto.error(f"Encountered an issue while getting offense type: {e}") return {} def get_offense_closing_reasons(client: Client, offenses: List[dict]) -> dict: """ Receives list of offenses, and performs API call to QRadar service to retrieve the closing reason names matching the closing reason IDs of the offenses. Args: client (Client): Client to perform the API request to QRadar. offenses (List[Dict]): List of all of the offenses. Returns: (Dict): Dictionary of {closing_reason_id: closing_reason_name} """ try: closing_reason_ids = { offense.get("closing_reason_id") for offense in offenses if offense.get("closing_reason_id") is not None } if not closing_reason_ids: return {} closing_reasons = client.closing_reasons_list( filter_=f"""id in ({",".join(map(str, closing_reason_ids))})""", fields="id,text" ) return {closing_reason.get("id"): closing_reason.get("text") for closing_reason in closing_reasons} except Exception as e: demisto.error(f"Encountered an issue while getting offense closing reasons: {e}") return {} def get_names_with_retries(func: Callable, *args, **kwargs) -> dict: """ A wrapper for retrying API calls with exponential backoff for getting names from IDs. Args: func (Callable): The API call to be executed and retried. *args: Positional arguments to pass to the API call. **kwargs: Keyword arguments to pass to the API call. Returns: dict: Dictionary of {id: name} from the API response, or an empty dictionary if all retries fail. """ # Retry logic with exponential backoff max_retries = CONNECTION_ERRORS_RETRIES # Use existing constant (5) base_delay = CONNECTION_ERRORS_INTERVAL # Use existing constant (1) last_exception = None # NOTE: Retry logic is essential here to prevent silent failures in name resolution. # Without retries, API call failures result in empty dict return, causing IDs (e.g., "6") # to be displayed instead of names (e.g., "ABC") in the relevant field. for attempt in range(max_retries): try: demisto.debug(f"Resolution attempt {attempt + 1}/{max_retries} to get names using {func.__name__}.") return func(*args, **kwargs) except Exception as e: last_exception = e attempt_msg = f"Resolution attempt {attempt + 1}/{max_retries} failed to get names using {func.__name__}." if attempt < max_retries - 1: # Calculate delay with exponential backoff delay = base_delay * (2**attempt) demisto.debug(f"{attempt_msg}: {str(e)}. Retrying in {delay} seconds...") time.sleep(delay) else: demisto.error(f"{attempt_msg}: {str(e)}. All retry attempts exhausted.") # If we reach here, all retries failed error_msg = f"Requests failed after {max_retries} attempts" if last_exception: error_msg += f". Last error: {str(last_exception)}" demisto.error(error_msg) demisto.info(f"Falling back to using IDs instead of names from {func.__name__}.") return {} def get_domain_names(client: Client, outputs: List[dict]) -> dict: """ Receives list of outputs, and performs API call to QRadar service to retrieve the domain names matching the domain IDs of the outputs. Includes retry logic and enhanced logging for better reliability. Args: client (Client): Client to perform the API request to QRadar. outputs (List[Dict]): List of all of the offenses. Returns: (Dict): Dictionary of {domain_id: domain_name} """ domain_ids = {offense.get("domain_id") for offense in outputs if offense.get("domain_id") is not None} if not domain_ids: demisto.debug("No domain IDs found in outputs for domain name enrichment") return {} domain_ids_str = ",".join(map(str, domain_ids)) demisto.debug(f"Attempting to resolve domain names for domain IDs: {domain_ids_str}") domains_info = get_names_with_retries(client.domains_list, filter_=f"id in ({domain_ids_str})", fields="id,name") if domains_info: domain_mapping = {domain_info.get("id"): domain_info.get("name") for domain_info in domains_info} demisto.debug(f"Successfully resolved {len(domain_mapping)} domain names: {domain_mapping}") return domain_mapping else: demisto.debug(f"Domain list API returned empty response for domain IDs: {domain_ids_str}") return {} def get_rules_names(client: Client, offenses: List[dict]) -> dict: """ Receives list of offenses, and performs API call to QRadar service to retrieve the rules names matching the rule IDs of the offenses. Args: client (Client): Client to perform the API request to QRadar. offenses (List[Dict]): List of all of the offenses. Returns: (Dict): Dictionary of {rule_id: rule_name} """ # Collect unique rule IDs rules_ids = {rule.get("id") for offense in offenses for rule in offense.get("rules", []) if rule.get("id") is not None} if not rules_ids: demisto.debug("No rule IDs found in offenses for rule name enrichment") return {} rules_ids_str = ",".join(map(str, rules_ids)) demisto.debug(f"Attempting to resolve rule names for rule IDs: {rules_ids_str}") rules = get_names_with_retries(client.rules_list, filter_=f"id in ({rules_ids_str})", fields="id,name") if rules: mapping = {rule.get("id"): rule.get("name") for rule in rules} demisto.debug(f"Successfully resolved {len(mapping)} rule names") return mapping else: demisto.debug(f"Rules API returned empty response for rule IDs: {rules_ids_str}") return {} def get_offense_addresses(client: Client, offenses: List[dict], is_destination_addresses: bool) -> dict: """ Receives list of offenses, and performs API call to QRadar service to retrieve the source IP values matching the source IPs IDs of the offenses. Args: client (Client): Client to perform the API request to QRadar. offenses (List[Dict]): List of all of the offenses. is_destination_addresses(bool): Whether addresses to enrich are destination IPs (or source). Returns: (Dict): Dictionary of {source_address_id: source_address_name}. """ address_type = "local_destination" if is_destination_addresses else "source" address_field = f"{address_type}_ip" address_list_field = f"{address_type}_address_ids" url_suffix = f"{address_type}_addresses" def get_addresses_for_batch(b: List): try: return client.get_addresses(url_suffix, f"""id in ({",".join(map(str, b))})""", f"id,{address_field}") except Exception as e: demisto.error(f"Failed getting address barch with error: {e}") return [] addresses_ids = [address_id for offense in offenses for address_id in offense.get(address_list_field, [])] # Submit addresses in batches to avoid overloading QRadar service addresses_batches = [get_addresses_for_batch(b) for b in batch(addresses_ids[:OFF_ENRCH_LIMIT], batch_size=int(BATCH_SIZE))] return { address_data.get("id"): address_data.get(address_field) for addresses_batch in addresses_batches for address_data in addresses_batch } def create_single_asset_for_offense_enrichment(asset: dict) -> dict: """ Recieves one asset, and returns the expected asset values for enriching offense. Args: asset (Dict): Asset to enrich the offense with Returns: (Dict): The enriched asset. """ interfaces = { "interfaces": [ { "mac_address": interface.get("mac_address"), "id": interface.get("id"), "ip_addresses": [ {"type": ip_add.get("type"), "value": ip_add.get("value")} for ip_add in interface.get("ip_addresses", []) ], } for interface in asset.get("interfaces", []) ] } properties = { prop.get("name"): prop.get("value") for prop in asset.get("properties", []) if "name" in prop and "value" in prop } offense_without_properties = {k: v for k, v in asset.items() if k != "properties"} return add_iso_entries_to_asset(dict(offense_without_properties, **properties, **interfaces)) def enrich_offense_with_assets(client: Client, offense_ips: List[str], assets_limit: int | None = 100) -> List[dict]: """ Receives list of offense's IPs, and performs API call to QRadar service to retrieve assets correlated to IPs given. Args: client (Client): Client to perform the API request to QRadar. offense_ips (List[str]): List of all of the offense's IPs. Returns: (List[Dict]): List of all the correlated assets. """ def get_assets_for_ips_batch(b: List): filter_query = " or ".join([f'interfaces contains ip_addresses contains value="{ip}"' for ip in b]) try: return client.assets_list(filter_=filter_query) except Exception as e: demisto.error(f"Failed getting assets for filter_query: {filter_query}. {e}") return [] offense_ips = [offense_ip for offense_ip in offense_ips if is_valid_ip(offense_ip)] # Submit addresses in batches to avoid overloading QRadar service assets: List = [] for b in batch(offense_ips[:OFF_ENRCH_LIMIT], batch_size=int(BATCH_SIZE)): assets.extend(get_assets_for_ips_batch(b)) if assets_limit and len(assets) >= assets_limit: assets = assets[:assets_limit] break return [create_single_asset_for_offense_enrichment(asset) for asset in assets] def enrich_offenses_result( client: Client, offenses: Any, enrich_ip_addresses: bool, enrich_assets: bool, assets_limit: int | None = None ) -> List[dict]: """ Receives list of offenses, and enriches the offenses with the following: - Changes offense_type value from the offense type ID to the offense type name. - Changes closing_reason_id value from closing reason ID to the closing reason name. - Adds a link to the URL of each offense. - Adds the domain name of the domain ID for each offense. - Adds to each rule of the offense its name. - Adds enrichment to each source/destination IP ID to its address (if enrich_ip_addresses is true). - Adds enrichment of assets to each offense (if enrich_assets is true). Args: client (Client): Client to perform the API calls. offenses (Any): List of all of the offenses to enrich. enrich_ip_addresses (bool): Whether to enrich the offense source/destination IP addresses. enrich_assets (bool): Whether to enrich the offense with assets. assets_limit (int): The limit of assets to enrich the offense with. Returns: (List[Dict]): The enriched offenses. """ if not isinstance(offenses, list): offenses = [offenses] print_debug_msg("Enriching offenses") offense_types_id_name_dict = get_offense_types(client, offenses) closing_reasons_id_name_dict = get_offense_closing_reasons(client, offenses) domain_id_name_dict = get_domain_names(client, offenses) if DOMAIN_ENRCH_FLG.lower() == "true" else {} rules_id_name_dict = get_rules_names(client, offenses) if RULES_ENRCH_FLG.lower() == "true" else {} source_addresses_id_ip_dict = get_offense_addresses(client, offenses, False) if enrich_ip_addresses else {} destination_addresses_id_ip_dict = get_offense_addresses(client, offenses, True) if enrich_ip_addresses else {} def create_enriched_offense(offense: dict) -> dict: link_to_offense_suffix = ( f"""/console/do/sem/offensesummary?appName=Sem&pageId=OffenseSummary&summaryId={offense.get("id")}""" ) offense_type = offense.get("offense_type") closing_reason_id = offense.get("closing_reason_id") domain_id = offense.get("domain_id") basic_enriches = { "offense_type": offense_types_id_name_dict.get(offense_type, offense_type), "closing_reason_id": closing_reasons_id_name_dict.get(closing_reason_id, closing_reason_id), "LinkToOffense": urljoin(client.server, link_to_offense_suffix), } domain_enrich = ( {"domain_name": domain_id_name_dict.get(domain_id, domain_id)} if DOMAIN_ENRCH_FLG.lower() == "true" and domain_id_name_dict.get(domain_id, domain_id) else {} ) rules_enrich = { "rules": [ {"id": rule.get("id"), "type": rule.get("type"), "name": rules_id_name_dict.get(rule.get("id"), rule.get("id"))} for rule in offense.get("rules", []) ] if RULES_ENRCH_FLG.lower() == "true" else {} } source_addresses_enrich = ( { "source_address_ids": [ source_addresses_id_ip_dict.get(source_address_id) for source_address_id in offense.get("source_address_ids", []) ] } if enrich_ip_addresses else {} ) destination_addresses_enrich = ( { "local_destination_address_ids": [ destination_addresses_id_ip_dict.get(destination_address_id) for destination_address_id in offense.get("local_destination_address_ids", []) ] } if enrich_ip_addresses else {} ) if enrich_assets: source_ips: List = source_addresses_enrich.get("source_address_ids", []) destination_ips: List = destination_addresses_enrich.get("local_destination_address_ids", []) all_ips: List = source_ips + destination_ips asset_enrich = {"assets": enrich_offense_with_assets(client, all_ips, assets_limit)} else: asset_enrich = {} return dict( offense, **basic_enriches, **domain_enrich, **rules_enrich, **source_addresses_enrich, **destination_addresses_enrich, **asset_enrich, ) result = [create_enriched_offense(offense) for offense in offenses] print_debug_msg("Enriched offenses successfully.") return result def enrich_asset_properties(properties: List, properties_to_enrich_dict: dict) -> dict: """ Receives list of properties of an asset, and properties to enrich, and returns a dict containing the enrichment Args: properties (List): List of properties of an asset. properties_to_enrich_dict (Dict): Properties to be enriched. Returns: (List[Dict]) List of new assets with enrichment. """ return { properties_to_enrich_dict.get(prop.get("name")): {"Value": prop.get("value"), "LastUser": prop.get("last_reported_by")} for prop in properties if prop.get("name") in properties_to_enrich_dict } def add_iso_entries_to_asset(asset: dict) -> dict: """ Transforms epoch entries to ISO entries in an asset. Requires a special treatment, because some of the usec entries are nested. Args: asset (Dict): Asset to transform its epoch entries to ISO. Returns: (Dict): Asset transformed. """ def get_asset_entry(k: str, v: Any): if k == "interfaces": return [ { k: ( get_time_parameter(v, iso_format=True) if k in USECS_ENTRIES else add_iso_entries_to_dict(v) if k == "ip_addresses" else v ) for k, v in interface.items() } for interface in v ] elif k == "properties": return add_iso_entries_to_dict(v) elif k in USECS_ENTRIES: return get_time_parameter(v, iso_format=True) else: return v return {k: get_asset_entry(k, v) for k, v in asset.items()} def enrich_assets_results(client: Client, assets: Any, full_enrichment: bool) -> List[dict]: """ Receives list of assets, and enriches each asset with 'Endpoint' entry containing the following: - IP addresses of all interfaces. - OS name. - MAC addresses of the interfaces, if full enrichment was requested. - Domain name if full enrichment was requested. - Properties enrichment. Args: client (Client): Client to perform API call to retrieve domain names corresponding to the domain IDs. assets (List[Dict]): List of assets to be enriched. full_enrichment (bool): Whether the asset should be full enriched. Returns: (List[Dict]) List of new assets with enrichment. """ domain_id_name_dict = get_domain_names(client, assets) if full_enrichment else {} def enrich_single_asset(asset: dict) -> dict: updated_asset = add_iso_entries_to_asset(asset) interfaces = updated_asset.get("interfaces", []) properties = updated_asset.get("properties", []) domain_id = updated_asset.get("domain_id") os_name = next((prop.get("value") for prop in properties if prop.get("name") == "Primary OS ID"), None) ip_enrichment = { "IPAddress": [ ip_add.get("value") for interface in interfaces for ip_add in interface.get("ip_addresses", []) if ip_add.get("value") ] } os_enrichment = {"OS": os_name} if os_name else {} mac_enrichment = ( {"MACAddress": [interface.get("mac_address") for interface in interfaces if interface.get("mac_address")]} if full_enrichment else {} ) domains_enrichment = {"Domain": domain_id_name_dict.get(domain_id, domain_id)} if full_enrichment and domain_id else {} basic_properties_enrichment = enrich_asset_properties(properties, ASSET_PROPERTIES_NAME_MAP) full_properties_enrichment = ( enrich_asset_properties(properties, FULL_ASSET_PROPERTIES_NAMES_MAP) if full_enrichment else {} ) enriched_asset = dict(asset, **basic_properties_enrichment, **full_properties_enrichment) return { "Asset": add_iso_entries_to_asset(enriched_asset), "Endpoint": dict(ip_enrichment, **os_enrichment, **mac_enrichment, **domains_enrichment), } return [enrich_single_asset(asset) for asset in assets] def get_minimum_id_to_fetch(highest_offense_id: int, user_query: Optional[str], first_fetch: str, client: Client) -> int: """ Receives the highest offense ID saved from last run, and user query. Checks if user query has a limitation for a minimum ID. If such ID exists, returns the maximum between 'highest_offense_id' and the minimum ID limitation received by the user query. Args: highest_offense_id (int): Minimum ID to fetch offenses by from last run. user_query (Optional[str]): User query for QRadar service. first_fetch (str): First fetch timestamp. client (Client): Client to perform the API calls. Returns: (int): The Minimum ID to fetch offenses by. """ if not highest_offense_id: highest_offense_id = get_min_id_from_first_fetch(first_fetch, client) if user_query: id_query = ID_QUERY_REGEX.search(user_query) if id_query: id_query_raw = id_query.group(0) operator = ">=" if ">=" in id_query_raw else ">" # safe to int parse without catch because regex checks for number user_offense_id = int(id_query.group(0).split(operator)[1].strip()) user_lowest_offense_id = user_offense_id if operator == ">" else user_offense_id - 1 print_debug_msg(f"Found ID in user query: {user_lowest_offense_id}, last highest ID: {highest_offense_id}") return max(highest_offense_id, user_lowest_offense_id) return highest_offense_id def get_min_id_from_first_fetch(first_fetch: str, client: Client): """ Receives first_fetch integration param and retrieve the lowest id (earliest offense) that was created after that time. Args: first_fetch (str): First fetch timestamp. client (Client): Client to perform the API calls. Returns: (int): The ID of the earliest offense created after first_fetch. """ filter_fetch_query = f"start_time>{convert_start_fetch_to_milliseconds(first_fetch)!s}" raw_offenses = client.offenses_list(filter_=filter_fetch_query, sort=ASCENDING_ID_ORDER, range_="items=0-0", fields="id") return int(raw_offenses[0].get("id")) - 1 if raw_offenses else 0 def arg_to_real_number(arg, arg_name=None, required=False): # type: (Any, Optional[str], bool) -> Optional[int | float] """Converts an XSOAR argument to a Python int or float This function acts exactly like CommonServerPython's arg_to_number, but is able to return float :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 | float`` if arg can be converted returns ``None`` if arg is ``None`` and required is set to ``False`` otherwise throws an Exception :rtype: ``Optional[int | float]`` """ if arg is None or arg == "": if required is True: if arg_name: raise ValueError(f'Missing "{arg_name}"') else: raise ValueError("Missing required argument") return None arg = encode_string_results(arg) if isinstance(arg, str): if arg.isdigit(): return int(arg) try: return float(arg) except Exception: if arg_name: raise ValueError(f'Invalid number: "{arg_name}"="{arg}"') else: raise ValueError(f'"{arg}" is not a valid number') if isinstance(arg, int): return arg if arg_name: raise ValueError(f'Invalid number: "{arg_name}"="{arg}"') else: raise ValueError(f'"{arg}" is not a valid number') def convert_start_fetch_to_milliseconds(fetch_start_time: str): """ Convert a timestamp string to milliseconds Args: fetch_start_time (str): First fetch timestamp. Returns: (int): time since (epoch - first_fetch) in milliseconds. """ date = dateparser.parse(fetch_start_time, settings={"TIMEZONE": "UTC"}) if date is None: # if date is None it means dateparser failed to parse it raise ValueError(f"Invalid first_fetch format: {fetch_start_time}") return int(date.timestamp() * 1000) def convert_dict_to_actual_values(input_dict: dict) -> dict[str, Any]: """ Recursively converts string representations of values in a dictionary to their actual data types. Args: input_dict (dict): A dictionary with string representations of values. Returns: dict: A dictionary with actual values (numbers, booleans, etc.). """ output_dict: dict[str, Any] = {} for key, value in input_dict.items(): if isinstance(value, dict): output_dict[key] = convert_dict_to_actual_values(value) elif isinstance(value, list): output_dict[key] = convert_list_to_actual_values(value) elif isinstance(value, str): try: # output_dict[key] = argToBoolean(value) output_dict[key] = arg_to_real_number(value) except ValueError: try: # output_dict[key] = arg_to_real_number(value) output_dict[key] = argToBoolean(value) except ValueError: output_dict[key] = value else: output_dict[key] = value return output_dict def convert_list_to_actual_values(input_list: list) -> list[Any]: """ Recursively converts string representations of values in a list to their actual data types. Args: input_list (list): A list with string representations of values. Returns: dict: A list with actual values (numbers, booleans, etc.). """ output_list: list[Any] = [] for value in input_list: if isinstance(value, dict): output_list.append(convert_dict_to_actual_values(value)) elif isinstance(value, list): output_list.append(convert_list_to_actual_values(value)) elif isinstance(value, str): try: output_list.append(argToBoolean(value)) except ValueError: try: output_list.append(arg_to_real_number(value)) except ValueError: output_list.append(value) else: output_list.append(value) return output_list def parse_log_source(create_args: dict[str, Any]): pp_pairs = create_args.get("protocol_parameters", "").split(",") protocol_parameters = [] group_ids = create_args.get("group_ids", "").split(",") if create_args.get("group_ids") else [] wincollect_external_destination_ids = ( create_args.get("wincollect_external_destination_ids", "").split(",") if create_args.get("group_ids") else [] ) for pair in pp_pairs: # Split the pair into name and value using '=' as delimiter name, value = pair.split("=") # Add the pair to the dictionary protocol_parameters.append({"name": name.strip(), "value": value.strip()}) return convert_dict_to_actual_values( { **create_args, "protocol_parameters": protocol_parameters, "group_ids": group_ids, "wincollect_external_destination_ids": wincollect_external_destination_ids, } ) def parse_partial_log_source(update_args: dict[str, Any]): protocol_parameters = update_args.get("protocol_parameters", "").split(",") if update_args.get("protocol_parameters") else [] group_ids = update_args.get("group_ids", "").split(",") if update_args.get("group_ids") else None wincollect_external_destination_ids = ( update_args.get("wincollect_external_destination_ids", "").split(",") if update_args.get("group_ids") else None ) if protocol_parameters: for pair in protocol_parameters: # Split the pair into name and value using '=' as delimiter name, value = pair.split("=") # Add the pair to the dictionary protocol_parameters.append({"name": name.strip(), "value": value.strip()}) log_source_str = {**update_args} if protocol_parameters: log_source_str["protocol_parameters"] = protocol_parameters if group_ids: log_source_str["group_ids"] = group_ids if wincollect_external_destination_ids: log_source_str["wincollect_external_destination_ids"] = wincollect_external_destination_ids return convert_dict_to_actual_values(log_source_str) def get_offense_enrichment(enrichment: str) -> tuple[bool, bool]: """ Receives enrichment asked by the user, returns true or false values indicating which enrichment should be done. Args: enrichment (Optional[str]): Enrichment argument. Returns: (bool, bool): Tuple of (ip_enrich, asset_enrich). """ if enrichment == "IPs And Assets": return True, True if enrichment == "IPs": return True, False return False, False def print_debug_msg(msg: str): """ Prints a message to debug with QRadarMsg prefix. Args: msg (str): Message to be logged. """ demisto.debug(f"QRadarMsg - {msg}") def is_reset_triggered(ctx: dict | None = None) -> bool: """ Checks if reset of the integration context has been made by the user. Because fetch is long-running, the user triggers a reset by calling 'qradar-reset-last-run', which sets 'reset' in the context. If found, we clear the key sub-dicts and 'samples', plus remove the 'reset' key. Returns True if a reset was triggered and handled, False otherwise. """ if not ctx: ctx = qradar_get_integration_context() # RESET_KEY must be True if 'qradar-reset-last-run' command was called if isinstance(ctx, dict) and ctx.get(RESET_KEY) is True: print_debug_msg("Reset fetch-incidents.") demisto.setLastRun({LAST_FETCH_KEY: 0}) ctx.pop(RESET_KEY, None) ctx[MIRRORED_OFFENSES_QUERIED_CTX_KEY] = {} ctx[MIRRORED_OFFENSES_FINISHED_CTX_KEY] = {} ctx[SAMPLE_INCIDENTS_KEY] = [] partial_changes = { # Explicitly remove RESET_KEY by setting it to None (will be handled by merge logic) RESET_KEY: None, MIRRORED_OFFENSES_QUERIED_CTX_KEY: ctx[MIRRORED_OFFENSES_QUERIED_CTX_KEY], MIRRORED_OFFENSES_FINISHED_CTX_KEY: ctx[MIRRORED_OFFENSES_FINISHED_CTX_KEY], SAMPLE_INCIDENTS_KEY: ctx[SAMPLE_INCIDENTS_KEY], } safely_update_context_data_partial( partial_changes, override_keys=[MIRRORED_OFFENSES_QUERIED_CTX_KEY, MIRRORED_OFFENSES_FINISHED_CTX_KEY, SAMPLE_INCIDENTS_KEY], ) return True return False def validate_long_running_params(params: dict) -> None: """ Receives params, checks whether the required parameters for long running execution is configured. Args: params (Dict): Cortex XSOAR params. Returns: (None): If all required params are set, raises DemistoException otherwise. """ for param_field, param_display in LONG_RUNNING_REQUIRED_PARAMS.items(): if param_field not in params: raise DemistoException( f"Parameter {param_display} is required when enabling long running execution. Please set a value for it." ) def get_cidrs_indicators(query): """Extracts cidrs from a query""" if not query: return [] res = demisto.searchIndicators(query=query) indicators = [] for indicator in res.get("iocs", []): if indicator.get("indicator_type").lower() == "cidr": indicators.append(indicator.get("value")) return indicators def verify_args_for_remote_network_cidr(cidrs_list, cidrs_from_query, name, group, fields): # verify that only one of the arguments is given if cidrs_list and cidrs_from_query: return "Cannot specify both cidrs and query arguments." # verify that at least one of the arguments is given if not cidrs_list and not cidrs_from_query: return "Must specify either cidrs or query arguments." # verify that the given cidrs are valid for cidr in cidrs_list: if not re.match(ipv4cidrRegex, cidr) and not re.match(ipv6cidrRegex, cidr): return f"{cidr} is not a valid CIDR." # verify that the given name and group are valid if not NAME_AND_GROUP_REGEX.match(name) or not NAME_AND_GROUP_REGEX.match(group): return "Name and group arguments only allow letters, numbers, '_' and '-'." fields_list = argToList(fields) if fields_list: possible_fields = ["id", "name", "group", "cidrs", "description"] for field in fields_list: if field not in possible_fields: return f"{field} is not a valid field. Possible fields are: {possible_fields}." return None return None def is_positive(*values: int | None) -> bool: # checks if all values are positive or None but not a negative numbers return all(value is None or value >= 1 for value in values) def verify_args_for_remote_network_cidr_list(limit, page, page_size, filter_, group, id_, name): # verify that the given limit and page and page_size are valid if not is_positive(limit, page, page_size): return "Limit, page and page_size arguments must be positive numbers." # verify that only one of the arguments is given if limit and (page or page_size): return "Please provide either limit argument or page and page_size arguments." # verify that if page are given, page_size is also given and vice versa if (page and not page_size) or (page_size and not page): return "Please provide both page and page_size arguments." # verify that only one of the arguments is given if filter_ and (group or id_ or name): return "You can not use filter argument with group, id or name arguments." return None """ COMMAND FUNCTIONS """ def test_module_command(client: Client, params: dict) -> 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. Args: client (Client): Client to perform the API calls. params (Dict): Demisto params. Returns: - (str): 'ok' if test passed - raises DemistoException if something had failed the test. """ try: ctx = qradar_get_integration_context() print_context_data_stats(ctx, "Test Module") is_long_running = params.get("longRunning") if is_long_running: validate_long_running_params(params) client.offenses_list(range_="items=0-0") message = "ok" except DemistoException as e: err_msg = str(e) if "unauthorized to access the requested resource" in err_msg or "No SEC header present in request" in err_msg: message = "Authorization Error: make sure credentials are correct." else: raise e return message def calculate_object_size(item: dict) -> int: """ Calculate the approximate size of an JSON serializable object in bytes for context storage. This function uses a multi-step process with granular error handling: 1. It first attempts to create a string using JSON serialization, which is precise. 2. If JSON serialization fails (e.g., due to non-serializable types), it falls back to using the basic `str()` representation. 3. It then attempts to encode the resulting string to UTF-8 to get the byte size. 4. If encoding fails (a rare case), it performs the encoding again but replaces any problematic characters to guarantee a result. Args: incident (dict): The JSON serializable object. Returns: int: The calculated or estimated size of the object in bytes. """ try: string_to_encode = json.dumps(item, default=str) except TypeError as e: print_debug_msg(f"Could not serialize object to JSON: {e}. Using fallback string representation.") string_to_encode = str(item) try: encoded_bytes = string_to_encode.encode("utf-8") return len(encoded_bytes) except UnicodeEncodeError as e: print_debug_msg(f"Could not encode string to UTF-8: {e}. Forcing encoding by replacing errors.") encoded_bytes_safe = string_to_encode.encode("utf-8", errors="replace") return len(encoded_bytes_safe) def is_incident_size_acceptable(incident: dict) -> bool: """ Check if an incident is small enough to be stored as a sample in the integration context. Args: incident (dict): The incident dictionary Returns: bool: True if incident size is acceptable, False otherwise """ size_bytes = calculate_object_size(incident) if size_bytes > MAX_SAMPLE_SIZE_BYTES: print_debug_msg( f"Incident {incident.get('name', 'Unknown')} size ({size_bytes / (1024 * 1024):.2f} MB) " f"exceeds maximum sample size ({MAX_SAMPLE_SIZE_MB} MB). Skipping from samples." ) return False return True def fetch_incidents_command() -> List[dict]: """ Fetch incidents implemented, for mapping purposes only. Returns list of samples saved by long running execution. Returns: (List[Dict]): List of incidents samples, limited to SAMPLE_SIZE. """ ctx = qradar_get_integration_context() samples = ctx.get(SAMPLE_INCIDENTS_KEY, []) # Enforce the sample size limit to prevent returning too many incidents return samples[:SAMPLE_SIZE] def create_search_with_retry( client: Client, fetch_mode: str, offense: dict, event_columns: str, events_limit: int, max_retries: int = EVENTS_SEARCH_TRIES, ) -> str: """ Creates a search to retrieve events for an offense. Has retry mechanism, because QRadar service tends to return random errors when it is loaded. Therefore, 'max_retries' retries will be made, to try avoid such cases as much as possible. Args: client (Client): Client to perform the API calls. fetch_mode (str): Which enrichment mode was requested. Can be 'Fetch With All Events', 'Fetch Correlation Events Only' offense (Dict): Offense ID to enrich with events. event_columns (str): Columns of the events to be extracted from query. events_limit (int): Maximum number of events to enrich the offense. max_retries (int): Number of retries. Returns: (str): The search id or `error` from `SearchQueryStatus` """ offense_id = offense["id"] for i in range(max_retries): search_id = create_events_search(client, fetch_mode, event_columns, events_limit, offense_id, offense["start_time"]) if search_id == QueryStatus.ERROR.value: print_debug_msg(f"Failed to create search for offense ID: {offense_id}. Retry number {i + 1}/{max_retries}.") print_debug_msg(traceback.format_exc()) else: return search_id print_debug_msg(f"Reached max retries for creating a search for offense: {offense_id}. Returning error.") return QueryStatus.ERROR.value def poll_offense_events( client: Client, search_id: str, should_get_events: bool, offense_id: int | None, ): try: print_debug_msg(f"Getting search status for {search_id}") search_status_response = client.search_status_get(search_id) print_debug_msg(f"Got search status for {search_id}") query_status = search_status_response.get("status") query_runtime = search_status_response.get("query_execution_time", "N/A") print_debug_msg(f"Search status for offense {offense_id} is {query_status}. Current time elapsed: {query_runtime}") if query_status in {"CANCELED", "ERROR"}: return [], QueryStatus.ERROR.value elif query_status == "COMPLETED": print_debug_msg(f"Search for offense {offense_id} is completed.") if not should_get_events: return [], QueryStatus.SUCCESS.value print_debug_msg(f"Getting events for offense {offense_id}") search_results_response = client.search_results_get(search_id) print_debug_msg(f"Http response: {search_results_response.get('http_response', 'Not specified - ok')}") events = search_results_response.get("events", []) sanitized_events = sanitize_outputs(events) print_debug_msg(f"Fetched events for offense {offense_id}.") return sanitized_events, QueryStatus.SUCCESS.value else: # still waiting for events return [], QueryStatus.WAIT.value except Exception as e: print_debug_msg( f"Error while fetching offense {offense_id} events, search_id: {search_id}. Error details: {e!s} \n" f"{traceback.format_exc()}" ) time.sleep(FAILURE_SLEEP) return [], QueryStatus.ERROR.value def poll_offense_events_with_retry( client: Client, search_id: str, offense_id: int, max_retries: int = EVENTS_POLLING_TRIES, ) -> tuple[List[dict], str]: """ Polls QRadar service for search ID given until status returned is within '{'CANCELED', 'ERROR', 'COMPLETED'}'. Afterwards, performs a call to retrieve the events returned by the search. Has retry mechanism, because QRadar service tends to return random errors when it is loaded. Therefore, 'max_retries' retries will be made, to try avoid such cases as much as possible. Args: client (Client): Client to perform the API calls. search_id (str): ID of the search to poll for its status. offense_id (int): ID of the offense to enrich with events returned by search. Used for logging purposes here. max_retries (int): Number of retries. Returns: (List[Dict], str): List of events returned by query. Returns empty list if number of retries exceeded limit, A failure message in case an error occurred. """ for retry in range(max_retries): print_debug_msg(f"Polling for events for offense {offense_id}. Retry number {retry + 1}/{max_retries}") events, status = poll_offense_events(client, search_id, should_get_events=True, offense_id=int(offense_id)) if status == QueryStatus.SUCCESS.value: return events, "" elif status == QueryStatus.ERROR.value: return [], "Error while getting events." # dont sleep in the last iteration if retry < max_retries - 1: time.sleep(EVENTS_INTERVAL_SECS) print_debug_msg(f"Max retries for getting events for offense {offense_id}. Cancel query search_id: {search_id}") # need to cancel query client.search_cancel(search_id=search_id) return [], "Fetching events is in progress" def enrich_offense_with_events(client: Client, offense: dict, fetch_mode: FetchMode, events_columns: str, events_limit: int): """ Enriches offense given with events. Has retry mechanism for events returned by query to QRadar. This is needed because events might not be indexed when performing the search, and QRadar will return less events than expected. Retry mechanism here meant to avoid such cases as much as possible Args: client (Client): Client to perform the API calls. offense (Dict): Offense to enrich with events. fetch_mode (str): Which enrichment mode was requested. Can be 'Fetch With All Events', 'Fetch Correlation Events Only' events_columns (str): Columns of the events to be extracted from query. events_limit (int): Maximum number of events to enrich the offense. Returns: (Dict): Enriched offense with events. """ offense_id = str(offense["id"]) events_count = offense.get("event_count", 0) events: List[dict] = [] failure_message = "" is_success = True for retry in range(EVENTS_SEARCH_TRIES): start_time = time.time() search_id = create_search_with_retry(client, fetch_mode, offense, events_columns, events_limit) if search_id == QueryStatus.ERROR.value: failure_message = "Search for events was failed." else: time.sleep(FETCH_INITIAL_SLEEP) events, failure_message = poll_offense_events_with_retry(client, search_id, int(offense_id)) events_fetched = get_num_events(events) offense["events_fetched"] = events_fetched offense["events"] = events if is_all_events_fetched(client, fetch_mode, offense_id, events_limit, events): break print_debug_msg( f"Not enough events were fetched for offense {offense_id}. Retrying in {FAILURE_SLEEP} seconds." f"Retry {retry + 1}/{EVENTS_SEARCH_TRIES}" ) time_elapsed = int(time.time() - start_time) # wait for the rest of the time time.sleep(max(EVENTS_SEARCH_RETRY_SECONDS - time_elapsed, 0)) else: print_debug_msg( f"Not all the events were fetched for offense {offense_id} (fetched {events_fetched}/{events_count}). " f"If mirroring is enabled, it will be queried again in mirroring." ) is_success = False mirroring_events_message = update_events_mirror_message( mirror_options=MIRROR_OFFENSE_AND_EVENTS, events_limit=events_limit, fetch_mode=fetch_mode, events_count=events_count, events_mirrored=events_fetched, events_mirrored_collapsed=len(events), failure_message=failure_message, offense_id=int(offense_id), ) offense["mirroring_events_message"] = mirroring_events_message return offense, is_success def get_num_events(events: list[dict]) -> int: return sum(int(event.get("eventcount", 1)) for event in events) def get_current_concurrent_searches(context_data: dict) -> int: """This will return the number of concurrent searches that are currently running. Args: context_data (dict): context data Returns: int: number of concurrent searches """ waiting_for_update = context_data.get(MIRRORED_OFFENSES_QUERIED_CTX_KEY, {}) # we need offenses which we have a search_id for it in QRadar return len([offense_id for offense_id, status in waiting_for_update.items() if status not in QueryStatus.values()]) def delete_offense_from_context(offense_id: str): """ Removes offense_id from MIRRORED_OFFENSES_QUERIED_CTX_KEY and MIRRORED_OFFENSES_FINISHED_CTX_KEY in a concurrency-safe manner, without overwriting unrelated data. """ ctx = qradar_get_integration_context() offenses_queried = ctx.get(MIRRORED_OFFENSES_QUERIED_CTX_KEY, {}) offenses_finished = ctx.get(MIRRORED_OFFENSES_FINISHED_CTX_KEY, {}) offenses_queried.pop(offense_id, None) offenses_finished.pop(offense_id, None) partial_changes = { MIRRORED_OFFENSES_QUERIED_CTX_KEY: offenses_queried, MIRRORED_OFFENSES_FINISHED_CTX_KEY: offenses_finished, } safely_update_context_data_partial( partial_changes, override_keys=list(partial_changes.keys()), # need to override (not merge!) since offense was deleted ) def is_all_events_fetched(client: Client, fetch_mode: FetchMode, offense_id: str, events_limit: int, events: list[dict]) -> bool: """ This function checks if all events were fetched for a specific offense. Args: client (Client): QRadar client offense_id (str): offense id of qradar events_limit (int): event limit parameter for the integration events (list[dict]): list of events fetched Returns: bool: True if all events were fetched, False otherwise """ if not offense_id: # if we don't have offense id, we can't know if we fetched all the events return True events_count = client.offenses_list(offense_id=int(offense_id)).get("event_count", 0) expected_events = min(events_count, events_limit) if events_limit else events_count num_events = get_num_events(events) print_debug_msg(f"Fetched {num_events}/{expected_events} events for offense {offense_id}") # if we're not fetching only correlation events, we can't know if we fetched all the events return num_events >= expected_events if fetch_mode == FetchMode.all_events else num_events > 0 def get_incidents_long_running_execution( client: Client, offenses_per_fetch: int, user_query: str, fetch_mode: str, events_columns: str, events_limit: int, ip_enrich: bool, asset_enrich: bool, last_highest_id: int, incident_type: Optional[str], mirror_direction: Optional[str], first_fetch: str, mirror_options: str, assets_limit: int, ) -> tuple[Optional[List[dict]], Optional[int]]: """ Gets offenses from QRadar service, and transforms them to incidents in a long running execution. Args: client (Client): Client to perform the API calls. offenses_per_fetch (int): Maximum number of offenses to be fetched. user_query (str): If given, the user filters for fetching offenses from QRadar service. fetch_mode (str): Fetch mode of the offenses. Can be 'Fetch Without Events', 'Fetch With All Events', 'Fetch Correlation Events Only' events_columns (str): Events columns to extract by search query for each offense. Only used when fetch mode is not 'Fetch Without Events'. events_limit (int): Number of events to be fetched for each offense. Only used when fetch mode is not 'Fetch Without Events'. ip_enrich (bool): Whether to enrich offense by changing IP IDs of each offense to its IP value. asset_enrich (bool): Whether to enrich offense with assets last_highest_id (int): The highest ID of all the offenses that have been fetched from QRadar service. incident_type (Optional[str]): Incident type. mirror_direction (Optional[str]): Whether mirror in is activated or not. first_fetch (str): First fetch timestamp. Returns: (List[Dict], int): List of the incidents, and the new highest ID for next fetch. (None, None): if reset was triggered """ offense_highest_id = get_minimum_id_to_fetch(last_highest_id, user_query, first_fetch, client) user_query = update_user_query(user_query) filter_fetch_query = f"id>{offense_highest_id}{user_query}" print_debug_msg(f"Filter query to QRadar: {filter_fetch_query}") range_max = offenses_per_fetch - 1 if offenses_per_fetch else MAXIMUM_OFFENSES_PER_FETCH - 1 range_ = f"items=0-{range_max}" # if it fails here we can't recover, retry again later raw_offenses = client.offenses_list(range_, filter_=filter_fetch_query, sort=ASCENDING_ID_ORDER) if raw_offenses: raw_offenses_len = len(raw_offenses) print_debug_msg(f"raw_offenses size: {raw_offenses_len}") else: print_debug_msg("empty raw_offenses") new_highest_offense_id = raw_offenses[-1].get("id") if raw_offenses else offense_highest_id print_debug_msg(f"New highest ID returned from QRadar offenses: {new_highest_offense_id}") offenses: list[dict] = [] if fetch_mode != FetchMode.no_events.value: futures = [] for offense in raw_offenses: futures.append( EXECUTOR.submit( enrich_offense_with_events, client=client, offense=offense, fetch_mode=fetch_mode, # type: ignore events_columns=events_columns, events_limit=events_limit, ) ) offenses_with_metadata = [future.result() for future in futures] offenses = [offense for offense, _ in offenses_with_metadata] if mirror_options == MIRROR_OFFENSE_AND_EVENTS: prepare_context_for_events(offenses_with_metadata) else: offenses = raw_offenses if is_reset_triggered(): return None, None offenses_with_mirror = ( [dict(offense, mirror_direction=mirror_direction, mirror_instance=demisto.integrationInstance()) for offense in offenses] if mirror_direction else offenses ) enriched_offenses = enrich_offenses_result(client, offenses_with_mirror, ip_enrich, asset_enrich, assets_limit) final_offenses = sanitize_outputs(enriched_offenses) incidents = create_incidents_from_offenses(final_offenses, incident_type) return incidents, new_highest_offense_id def prepare_context_for_events(offenses_with_metadata): """ For any offense that wasn't successfully enriched, mark it in MIRRORED_OFFENSES_QUERIED_CTX_KEY as WAIT. Uses partial merge so as not to overwrite other keys. """ ctx = qradar_get_integration_context() mirrored_offenses_queried = ctx.get(MIRRORED_OFFENSES_QUERIED_CTX_KEY, {}) for offense, is_success in offenses_with_metadata: if not is_success: offense_id = str(offense.get("id")) mirrored_offenses_queried[offense_id] = QueryStatus.WAIT.value partial_changes = {MIRRORED_OFFENSES_QUERIED_CTX_KEY: mirrored_offenses_queried} safely_update_context_data_partial(partial_changes) def create_incidents_from_offenses(offenses: List[dict], incident_type: Optional[str]) -> List[dict]: """ Transforms list of offenses given into incidents for Demisto. Args: offenses (List[Dict]): List of the offenses to transform into incidents. incident_type (Optional[str]): Incident type to be used for each incident. Returns: (List[Dict]): Incidents list. """ print_debug_msg(f"Creating {len(offenses)} incidents") return [ { # NOTE: incident name will be updated in mirroring also with incoming mapper. "name": f"""{offense.get("id")} {offense.get("description", "")}""", "rawJSON": json.dumps(offense), "occurred": get_time_parameter(offense.get("start_time"), iso_format=True), "type": incident_type, "haIntegrationEventID": str(offense.get("id")), } for offense in offenses ] def print_context_data_stats(context_data: dict, stage: str) -> set[str]: """Print debug message with information about mirroring events. Args: context_data: The integration context data. stage: A prefix for the debug message. Returns: The ids of the mirrored offenses being currently processed. """ if MIRRORED_OFFENSES_QUERIED_CTX_KEY not in context_data or MIRRORED_OFFENSES_FINISHED_CTX_KEY not in context_data: raise ValueError( f"Context data is missing keys: {MIRRORED_OFFENSES_QUERIED_CTX_KEY} or {MIRRORED_OFFENSES_FINISHED_CTX_KEY}" ) if not context_data: print_debug_msg("Not printing stats") return set() finished_queries = context_data.get(MIRRORED_OFFENSES_FINISHED_CTX_KEY, {}) waiting_for_update = context_data.get(MIRRORED_OFFENSES_QUERIED_CTX_KEY, {}) print_debug_msg(f"{finished_queries=}") print_debug_msg(f"{waiting_for_update=}") last_fetch_key = context_data.get(LAST_FETCH_KEY, "Missing") last_mirror_update = context_data.get(LAST_MIRROR_KEY, 0) last_mirror_update_closed = context_data.get(LAST_MIRROR_CLOSED_KEY, 0) concurrent_mirroring_searches = get_current_concurrent_searches(context_data) samples = context_data.get(SAMPLE_INCIDENTS_KEY, []) sample_length = 0 if samples: sample_length = len(samples[0]) not_updated_ids = list(waiting_for_update) finished_queries_ids = list(finished_queries) print_debug_msg( f"Context Data Stats: {stage}\n Finished Offenses (id): {finished_queries_ids}" f"\n Offenses ids waiting for update: {not_updated_ids}" f"\n Concurrent mirroring events searches: {concurrent_mirroring_searches}" f"\n Last Fetch Key {last_fetch_key}, Last mirror update {last_mirror_update}, " f"Last mirror update closed: {last_mirror_update_closed}, " f"sample length {sample_length}" ) return set(not_updated_ids + finished_queries_ids) def perform_long_running_loop( client: Client, offenses_per_fetch: int, fetch_mode: str, user_query: str, events_columns: str, events_limit: int, ip_enrich: bool, asset_enrich: bool, incident_type: Optional[str], mirror_direction: Optional[str], first_fetch: str, mirror_options: str, assets_limit: int, long_running_container_id: str, ): context_data = qradar_get_integration_context() if is_reset_triggered(context_data): last_highest_id = 0 print_debug_msg("Reset was triggered. Set last highest ID to 0.") else: last_highest_id = int(context_data.get(LAST_FETCH_KEY, 0)) print_debug_msg(f"Got last highest ID in context: {last_highest_id}.") print_debug_msg(f"Starting fetch loop. Fetch mode: {fetch_mode} on Container:{long_running_container_id}.") incidents, new_highest_id = get_incidents_long_running_execution( client=client, offenses_per_fetch=offenses_per_fetch, user_query=user_query, fetch_mode=fetch_mode, events_columns=events_columns, events_limit=events_limit, ip_enrich=ip_enrich, asset_enrich=asset_enrich, last_highest_id=last_highest_id, incident_type=incident_type, mirror_direction=mirror_direction, first_fetch=first_fetch, mirror_options=mirror_options, assets_limit=assets_limit, ) print_debug_msg(f"Got incidents, Creating incidents and updating context data. new highest id is {new_highest_id}") # Refresh context to see if something changed in parallel context_data = qradar_get_integration_context() if incidents and new_highest_id: # Filter incidents that are small enough to store as samples filtered_incidents = [incident for incident in incidents if is_incident_size_acceptable(incident)] incident_batch_for_sample = ( filtered_incidents[:SAMPLE_SIZE] if filtered_incidents else context_data.get(SAMPLE_INCIDENTS_KEY, [])[:SAMPLE_SIZE] ) if len(filtered_incidents) < len(incidents): skipped_count = len(incidents) - len(filtered_incidents) print_debug_msg(f"Skipped {skipped_count} incident(s) from samples due to size constraints.") # Actually create the incidents in XSOAR demisto.createIncidents(incidents, {LAST_FETCH_KEY: str(new_highest_id)}) partial_changes = {} if incident_batch_for_sample: partial_changes[SAMPLE_INCIDENTS_KEY] = incident_batch_for_sample # Always update LAST_FETCH_KEY partial_changes[LAST_FETCH_KEY] = int(new_highest_id) # Merge changes so we don't overwrite other subkeys safely_update_context_data_partial(partial_changes) print_debug_msg( f"Successfully Created {len(incidents)} incidents. Incidents created: {[incident['name'] for incident in incidents]}" ) def recover_from_last_run(ctx: dict | None = None): """ This recovers the integration context from the last run, if there is an inconsistency between demisto.getLastRun() and the context. This can happen when the container crashes after demisto.createIncidents but before the context is updated. """ if not ctx: ctx = get_integration_context() assert isinstance(ctx, dict) last_run = demisto.getLastRun() or {} last_highest_id_last_run = int(last_run.get(LAST_FETCH_KEY, 0)) print_debug_msg(f"Last highest ID from last run: {last_highest_id_last_run}") last_highest_id_context = int(ctx.get(LAST_FETCH_KEY, 0)) if last_highest_id_last_run != last_highest_id_context and last_highest_id_last_run > 0: # There's an inconsistency: we want to force the integration context to reflect last_run's ID. print_debug_msg( f"Updating context data with last highest ID from last run: {last_highest_id_last_run}. " f"ID from context: {last_highest_id_context}" ) partial_changes = { LAST_FETCH_KEY: last_highest_id_last_run, SAMPLE_INCIDENTS_KEY: ctx.get(SAMPLE_INCIDENTS_KEY, [])[:SAMPLE_SIZE], } safely_update_context_data_partial(partial_changes) print_debug_msg(f"Updated context last-fetch key from {last_highest_id_context} to {last_highest_id_last_run}.") def long_running_execution_command(client: Client, params: dict): """ Long running execution of fetching incidents from QRadar service. Will continue to fetch in an infinite loop offenses from QRadar, Enriching each offense with events/IPs/assets according to the configurations given in Demisto params. transforming the offenses into incidents and sending them to Demisto to save the incidents. Args: client (Client): Client to perform API calls. params (Dict): Demisto params. """ global EVENTS_SEARCH_TRIES validate_long_running_params(params) fetch_mode = params.get("fetch_mode", "") first_fetch = params.get("first_fetch", "3 days") ip_enrich, asset_enrich = get_offense_enrichment(params.get("enrichment", "IPs And Assets")) offenses_per_fetch = int(params.get("offenses_per_fetch") or DEFAULT_OFFENSES_PER_FETCH) user_query = params.get("query", "") events_columns = params.get("events_columns") or DEFAULT_EVENTS_COLUMNS events_limit = int(params.get("events_limit") or DEFAULT_EVENTS_LIMIT) incident_type = params.get("incident_type") mirror_options = params.get("mirror_options", DEFAULT_MIRRORING_DIRECTION) mirror_direction = MIRROR_DIRECTION.get(mirror_options) mirror_options = params.get("mirror_options", "") assets_limit = int(params.get("limit_assets", DEFAULT_ASSETS_LIMIT)) if not argToBoolean(params.get("retry_events_fetch", True)): EVENTS_SEARCH_TRIES = 1 context_data = qradar_get_integration_context() is_reset_triggered(context_data) recover_from_last_run(context_data) long_running_container_id = str(uuid.uuid4()) print_debug_msg(f"Starting container with UUID: {long_running_container_id}") while True: try: perform_long_running_loop( client=client, offenses_per_fetch=offenses_per_fetch, fetch_mode=fetch_mode, user_query=user_query, events_columns=events_columns, events_limit=events_limit, ip_enrich=ip_enrich, asset_enrich=asset_enrich, incident_type=incident_type, mirror_direction=mirror_direction, first_fetch=first_fetch, mirror_options=mirror_options, assets_limit=assets_limit, long_running_container_id=long_running_container_id, ) demisto.updateModuleHealth("") except Exception as e: msg = f"Error occurred during long running loop: {e}" demisto.updateModuleHealth(msg) demisto.error(msg) demisto.error(traceback.format_exc()) finally: print_debug_msg("Finished fetch loop") time.sleep(FETCH_SLEEP) def qradar_offenses_list_command(client: Client, args: dict) -> CommandResults: """ Retrieves list of offenses from QRadar service. possible arguments: - offense_id: Retrieves details of the specific offense that corresponds to the ID given. - range: Range of offenses to return (e.g.: 0-20, 3-5, 3-3). - filter: Query filter to filter results returned by QRadar service. see https://www.ibm.com/support/knowledgecenter/SS42VS_SHR/com.ibm.qradarapi.doc/c_rest_api_filtering.html for more details. - fields: If used, will filter all fields except for the specified ones. Use this parameter to specify which fields you would like to get back in the response. Fields that are not explicitly named are excluded. Args: client (Client): QRadar client to perform the API call. args (Dict): Demisto args. Returns: CommandResults. """ offense_id = args.get("offense_id") range_ = f"""items={args.get("range", DEFAULT_RANGE_VALUE)}""" filter_ = args.get("filter") fields = args.get("fields") ip_enrich, asset_enrich = get_offense_enrichment(args.get("enrichment", "None")) # if this call fails, raise an error and stop command execution response = client.offenses_list(range_, offense_id, filter_, fields) enriched_outputs = enrich_offenses_result(client, response, ip_enrich, asset_enrich) final_outputs = sanitize_outputs(enriched_outputs, OFFENSE_OLD_NEW_NAMES_MAP) headers = build_headers(["ID", "Description", "OffenseType", "Status", "Severity"], set(OFFENSE_OLD_NEW_NAMES_MAP.values())) return CommandResults( readable_output=tableToMarkdown("Offenses List", final_outputs, headers=headers, removeNull=True), outputs_prefix="QRadar.Offense", outputs_key_field="ID", outputs=final_outputs, raw_response=response, ) def qradar_offense_update_command(client: Client, args: dict) -> CommandResults: """ Updates offense that corresponds to the given offense ID. possible arguments: - offense_id (Required): Update offense that corresponds to ID given. - protected: Whether the offense is protected. - follow_up: Whether the offense should be marked for follow up. - status: Status of the offense. One of 'OPEN', 'HIDDEN', 'CLOSED'. - closing_reason_id: The ID of the reason the offense was closed. full list of closing reason IDs, full list of closing reason IDs can be retrieved by 'qradar-closing-reasons' command. - assigned_to: The user whom to assign the offense to. - fields: If used, will filter all fields except for the specified ones. Use this parameter to specify which fields you would like to get back in the response. Fields that are not explicitly named are excluded. Args: client (Client): QRadar client to perform the API call. args (Dict): Demisto args. Returns: CommandResults. """ offense_id: int = int(args["offense_id"]) protected = args.get("protected") follow_up = args.get("follow_up") closing_reason_name = args.get("closing_reason_name") status = args.get("status") closing_reason_id = args.get("closing_reason_id") if status == "CLOSED" and (not closing_reason_id and not closing_reason_name): raise DemistoException( """Closing reason ID must be provided when closing an offense. Available closing reasons can be achieved by 'qradar-closing-reasons' command.""" ) if closing_reason_name: # if this call fails, raise an error and stop command execution closing_reasons_list = client.closing_reasons_list(include_deleted=True, include_reserved=True) for closing_reason in closing_reasons_list: if closing_reason.get("text") == closing_reason_name: closing_reason_id = closing_reason.get("id") if not closing_reason_id: raise DemistoException( f"Could not find closing reason name {closing_reason_name}. Please provide a valid" " closing reason name. Closing reasons can be retrieved by running the " "qradar-closing-reasons command." ) assigned_to = args.get("assigned_to") fields = args.get("fields") ip_enrich, asset_enrich = get_offense_enrichment(args.get("enrichment", "None")) # if this call fails, raise an error and stop command execution response = client.offense_update(offense_id, protected, follow_up, status, closing_reason_id, assigned_to, fields) enriched_outputs = enrich_offenses_result(client, response, ip_enrich, asset_enrich) final_outputs = sanitize_outputs(enriched_outputs, OFFENSE_OLD_NEW_NAMES_MAP) headers = build_headers(["ID", "Description", "OffenseType", "Status", "Severity"], set(OFFENSE_OLD_NEW_NAMES_MAP.values())) return CommandResults( readable_output=tableToMarkdown("offense Update", final_outputs, headers, removeNull=True), outputs_prefix="QRadar.Offense", outputs_key_field="ID", outputs=final_outputs, raw_response=response, ) def qradar_closing_reasons_list_command(client: Client, args: dict) -> CommandResults: """ Retrieves list of closing reasons from QRadar service. possible arguments: - closing_reason_id: Retrieves details of the specific closing reason that corresponds to the ID given. - range: Range of offenses to return (e.g.: 0-20, 3-5, 3-3). - filter: Query filter to filter results returned by QRadar service. see https://www.ibm.com/support/knowledgecenter/SS42VS_SHR/com.ibm.qradarapi.doc/c_rest_api_filtering.html for more details. - fields: If used, will filter all fields except for the specified ones. Use this parameter to specify which fields you would like to get back in the response. Fields that are not explicitly named are excluded. Args: client (Client): QRadar client to perform the API call. args (Dict): Demisto args. Returns: CommandResults. """ closing_reason_id = args.get("closing_reason_id") include_reserved = argToBoolean(args.get("include_reserved", False)) include_deleted = argToBoolean(args.get("include_deleted", False)) range_ = f"""items={args.get("range", DEFAULT_RANGE_VALUE)}""" filter_ = args.get("filter") fields = args.get("fields") # if this call fails, raise an error and stop command execution response = client.closing_reasons_list(closing_reason_id, include_reserved, include_deleted, range_, filter_, fields) outputs = sanitize_outputs(response, CLOSING_REASONS_RAW_FORMATTED) headers = build_headers(["ID", "Name"], set(CLOSING_REASONS_RAW_FORMATTED.values())) return CommandResults( readable_output=tableToMarkdown("Closing Reasons", outputs, headers=headers, removeNull=True), outputs_prefix="QRadar.Offense.ClosingReasons", outputs_key_field="ID", outputs=outputs, raw_response=response, ) def qradar_offense_notes_list_command(client: Client, args: dict) -> CommandResults: """ Retrieves list of notes corresponding to the ID of the offense ID given from QRadar service. possible arguments: - offense_id: The offense ID to retrieve the notes for. - note_id: The note ID to its details. - range: Range of notes to return for the offense corresponding to the offense ID (e.g.: 0-20, 3-5, 3-3). - filter: Query filter to filter results returned by QRadar service. see https://www.ibm.com/support/knowledgecenter/SS42VS_SHR/com.ibm.qradarapi.doc/c_rest_api_filtering.html for more details. - fields: If used, will filter all fields except for the specified ones. Use this parameter to specify which fields you would like to get back in the response. Fields that are not explicitly named are excluded. Args: client (Client): QRadar client to perform the API call. args (Dict): Demisto args. Returns: CommandResults. """ offense_id: int = int(args["offense_id"]) note_id = args.get("note_id") range_ = f"""items={args.get("range", DEFAULT_RANGE_VALUE)}""" filter_ = args.get("filter") fields = args.get("fields") # if this call fails, raise an error and stop command execution response = client.offense_notes_list(offense_id, range_, note_id, filter_, fields) outputs = sanitize_outputs(response, NOTES_RAW_FORMATTED) headers = build_headers(["ID", "Text", "CreatedBy", "CreateTime"], set(NOTES_RAW_FORMATTED.values())) return CommandResults( readable_output=tableToMarkdown(f"Offense Notes List For Offense ID {offense_id}", outputs, headers, removeNull=True), outputs_prefix="QRadar.Note", outputs_key_field="ID", outputs=outputs, raw_response=response, ) def qradar_offense_notes_create_command(client: Client, args: dict) -> CommandResults: """ Create a new note for the offense corresponding to the given offense ID with the note text given to QRadar service. possible arguments: - offense_id: The offense ID to add note to. - note_text: The note text. - fields: If used, will filter all fields except for the specified ones. Use this parameter to specify which fields you would like to get back in the response. Fields that are not explicitly named are excluded. Args: client (Client): QRadar client to perform the API call. args (Dict): Demisto args. Returns: CommandResults. """ offense_id: int = int(args["offense_id"]) note_text: str = args.get("note_text", "") fields = args.get("fields") # if this call fails, raise an error and stop command execution response = client.offense_notes_create(offense_id, note_text, fields) outputs = sanitize_outputs(response, NOTES_RAW_FORMATTED) headers = build_headers(["ID", "Text", "CreatedBy", "CreateTime"], set(NOTES_RAW_FORMATTED.values())) return CommandResults( readable_output=tableToMarkdown("Create Note", outputs, headers, removeNull=True), outputs_prefix="QRadar.Note", outputs_key_field="ID", outputs=outputs, raw_response=response, ) def qradar_rules_list_command(client: Client, args: dict) -> CommandResults: """ Retrieves list of rules from QRadar service. possible arguments: - rule_id: Retrieves details of the specific rule that corresponds to the ID given. - rule_type: Retrieves rules corresponding to the given rule type. - range: Range of offenses to return (e.g.: 0-20, 3-5, 3-3). - filter: Query filter to filter results returned by QRadar service. see https://www.ibm.com/support/knowledgecenter/SS42VS_SHR/com.ibm.qradarapi.doc/c_rest_api_filtering.html for more details. - fields: If used, will filter all fields except for the specified ones. Use this parameter to specify which fields you would like to get back in the response. Fields that are not explicitly named are excluded. Args: client (Client): QRadar client to perform the API call. args (Dict): Demisto args. Returns: CommandResults. """ rule_id = args.get("rule_id") rule_type = args.get("rule_type") range_ = f"""items={args.get("range", DEFAULT_RANGE_VALUE)}""" filter_ = args.get("filter") fields = args.get("fields") if not filter_ and rule_type: filter_ = f"type={rule_type}" # if this call fails, raise an error and stop command execution response = client.rules_list(rule_id, range_, filter_, fields) outputs = sanitize_outputs(response, RULES_RAW_FORMATTED) headers = build_headers(["ID", "Name", "Type"], set(RULES_RAW_FORMATTED.values())) return CommandResults( readable_output=tableToMarkdown("Rules List", outputs, headers=headers, removeNull=True), outputs_prefix="QRadar.Rule", outputs_key_field="ID", outputs=outputs, raw_response=response, ) def qradar_rule_groups_list_command(client: Client, args: dict) -> CommandResults: """ Retrieves list of rule groups from QRadar service. possible arguments: - rule_group_id: Retrieves details of the specific rule group that corresponds to the ID given. - range: Range of offenses to return (e.g.: 0-20, 3-5, 3-3). - filter: Query filter to filter results returned by QRadar service. see https://www.ibm.com/support/knowledgecenter/SS42VS_SHR/com.ibm.qradarapi.doc/c_rest_api_filtering.html for more details. - fields: If used, will filter all fields except for the specified ones. Use this parameter to specify which fields you would like to get back in the response. Fields that are not explicitly named are excluded. Args: client (Client): QRadar client to perform the API call. args (Dict): Demisto args. Returns: CommandResults. """ rule_group_id = arg_to_number(args.get("rule_group_id")) range_ = f"""items={args.get("range", DEFAULT_RANGE_VALUE)}""" filter_ = args.get("filter") fields = args.get("fields") # if this call fails, raise an error and stop command execution response = client.rule_groups_list(range_, rule_group_id, filter_, fields) outputs = sanitize_outputs(response, RULES_GROUP_RAW_FORMATTED) headers = build_headers(["ID", "Name", "Description", "Owner"], set(RULES_GROUP_RAW_FORMATTED.values())) return CommandResults( readable_output=tableToMarkdown("Rules Group List", outputs, headers, removeNull=True), outputs_prefix="QRadar.RuleGroup", outputs_key_field="ID", outputs=outputs, raw_response=response, ) def qradar_assets_list_command(client: Client, args: dict) -> CommandResults: """ Retrieves list of assets from QRadar service. possible arguments: - asset_id: Retrieves details of the specific asset that corresponds to the ID given. - range: Range of offenses to return (e.g.: 0-20, 3-5, 3-3). - filter: Query filter to filter results returned by QRadar service. see https://www.ibm.com/support/knowledgecenter/SS42VS_SHR/com.ibm.qradarapi.doc/c_rest_api_filtering.html for more details. - fields: If used, will filter all fields except for the specified ones. Use this parameter to specify which fields you would like to get back in the response. Fields that are not explicitly named are excluded. Args: client (Client): QRadar client to perform the API call. args (Dict): Demisto args. Returns: CommandResults. """ asset_id = args.get("asset_id") range_ = f"""items={args.get("range", DEFAULT_RANGE_VALUE)}""" filter_ = args.get("filter") fields = args.get("fields") # If asset ID was given, override filter if both filter and asset ID were given. if asset_id: filter_ = f"id={asset_id}" full_enrichment = bool(asset_id) # if this call fails, raise an error and stop command execution response = client.assets_list(range_, filter_, fields) enriched_outputs = enrich_assets_results(client, response, full_enrichment) assets_results = {} assets_hr = [] endpoints = [] for output in enriched_outputs: output["Asset"]["hostnames"] = add_iso_entries_to_dict(output.get("Asset", {}).get("hostnames", [])) output["Asset"]["users"] = add_iso_entries_to_dict(output.get("Asset", {}).get("users", [])) output["Asset"]["products"] = add_iso_entries_to_dict(output.get("Asset", {}).get("products", [])) output["Asset"] = sanitize_outputs(output.get("Asset"), ASSET_RAW_FORMATTED)[0] assets_hr.append(output["Asset"]) assets_results[f"""QRadar.Asset(val.ID === "{output["Asset"]["ID"]}")"""] = output["Asset"] sanitized_endpoint = remove_empty_elements(output.get("Endpoint", {})) if sanitized_endpoint: endpoints.append(sanitized_endpoint) asset_human_readable = tableToMarkdown("Assets List", assets_hr, removeNull=True) endpoints_human_readable = tableToMarkdown("Endpoints", endpoints, removeNull=True) if endpoints: assets_results["Endpoint"] = endpoints return CommandResults( readable_output=asset_human_readable + endpoints_human_readable, outputs=assets_results, raw_response=response ) def qradar_saved_searches_list_command(client: Client, args: dict) -> CommandResults: """ Retrieves list of saved searches from QRadar service. possible arguments: - saved_search_id: Retrieves details of the specific saved search that corresponds to the ID given. - range: Range of offenses to return (e.g.: 0-20, 3-5, 3-3). - filter: Query filter to filter results returned by QRadar service. see https://www.ibm.com/support/knowledgecenter/SS42VS_SHR/com.ibm.qradarapi.doc/c_rest_api_filtering.html for more details. - fields: If used, will filter all fields except for the specified ones. Use this parameter to specify which fields you would like to get back in the response. Fields that are not explicitly named are excluded. Args: client (Client): QRadar client to perform the API call. args (Dict): Demisto args. Returns: CommandResults. """ saved_search_id = args.get("saved_search_id") timeout: Optional[int] = arg_to_number(args.get("timeout", DEFAULT_TIMEOUT_VALUE)) range_ = f"""items={args.get("range", DEFAULT_RANGE_VALUE)}""" filter_ = args.get("filter") fields = args.get("fields") # if this call fails, raise an error and stop command execution response = client.saved_searches_list(range_, timeout, saved_search_id, filter_, fields) outputs = sanitize_outputs(response, SAVED_SEARCH_RAW_FORMATTED) headers = build_headers(["ID", "Name", "Description"], set(SAVED_SEARCH_RAW_FORMATTED.values())) return CommandResults( readable_output=tableToMarkdown("Saved Searches List", outputs, headers, removeNull=True), outputs_prefix="QRadar.SavedSearch", outputs_key_field="ID", outputs=outputs, raw_response=response, ) def qradar_searches_list_command(client: Client, args: dict) -> CommandResults: """ Retrieves list of searches IDs from QRadar service. possible arguments: - range: Range of offenses to return (e.g.: 0-20, 3-5, 3-3). - filter: Query filter to filter results returned by QRadar service. see https://www.ibm.com/support/knowledgecenter/SS42VS_SHR/com.ibm.qradarapi.doc/c_rest_api_filtering.html for more details. Args: client (Client): QRadar client to perform the API call. args (Dict): Demisto args. Returns: CommandResults. """ range_ = f"""items={args.get("range", DEFAULT_RANGE_VALUE)}""" filter_ = args.get("filter") # if this call fails, raise an error and stop command execution response = client.searches_list(range_, filter_) outputs = [{"SearchID": search_id} for search_id in response] return CommandResults( readable_output=tableToMarkdown("Search ID List", outputs), outputs_prefix="QRadar.SearchID", outputs_key_field="SearchID", outputs=outputs, raw_response=response, ) def qradar_search_create_command(client: Client, params: dict, args: dict) -> CommandResults: """ Create a search in QRadar service. possible arguments: - query_expression: The AQL query to execute. Mutually exclusive with saved_search_id. - saved_search_id: Saved search ID to execute. Mutually exclusive with query_expression. Args: client (Client): QRadar client to perform the API call. params (Dict): Demisto params. args (Dict): Demisto args. Returns: CommandResults. """ offense_id = args.get("offense_id", "") events_columns = args.get("events_columns", params.get("events_columns")) or DEFAULT_EVENTS_COLUMNS events_limit = args.get("events_limit", params.get("events_limit")) fetch_mode = args.get("fetch_mode", params.get("fetch_mode")) start_time = args.get("start_time") query_expression = args.get("query_expression") saved_search_id = args.get("saved_search_id") if not query_expression and not saved_search_id and not offense_id: raise DemistoException("Please provide one of the following args: `query_expression`, `saved_search_id` or `offense_id`.") if query_expression and offense_id: raise DemistoException("Could not use both `query_expression` and `offense_id`.") # if this call fails, raise an error and stop command execution if query_expression or saved_search_id: try: response = client.search_create(query_expression, saved_search_id) except Exception as e: if query_expression: raise DemistoException(f"Could not create search for query: {query_expression}.") from e if saved_search_id: raise DemistoException(f"Could not create search for saved_search_id: {saved_search_id}.") from e else: response = create_events_search( client, fetch_mode, events_columns, events_limit, int(offense_id), start_time, return_raw_response=True ) if response == QueryStatus.ERROR.value: raise DemistoException(f"Could not create events search for offense_id: {offense_id}.") outputs = sanitize_outputs(response, SEARCH_RAW_FORMATTED) return CommandResults( readable_output=tableToMarkdown("Create Search", outputs), outputs_prefix="QRadar.Search", outputs_key_field="ID", outputs=outputs, raw_response=response, ) def qradar_search_status_get_command(client: Client, args: dict) -> CommandResults: """ Retrieves search status from QRadar service. possible arguments: - search_id (Required): The search ID to retrieve its status. Args: client (Client): QRadar client to perform the API call. args (Dict): Demisto args. Returns: CommandResults. """ search_id: str = args.get("search_id", "") # if this call fails, raise an error and stop command execution response = client.search_status_get(search_id) outputs = sanitize_outputs(response, SEARCH_RAW_FORMATTED) return CommandResults( readable_output=tableToMarkdown(f"Search Status For Search ID {search_id}", outputs), outputs_prefix="QRadar.Search", outputs_key_field="ID", outputs=outputs, raw_response=response, ) def qradar_search_delete_command(client: Client, args: dict) -> CommandResults: """ Delete search from QRadar service. possible arguments: - search_id (Required): The search ID to delete. Args: client (Client): QRadar client to perform the API call. args (Dict): Demisto args. Returns: CommandResults. """ search_id: str = args.get("search_id", "") # if this call fails, raise an error and stop command execution response = client.search_delete(search_id) return CommandResults(readable_output=f"Search ID {search_id} was successfully deleted.", raw_response=response) def qradar_search_cancel_command(client: Client, args: dict) -> CommandResults: """ Cancelled search from QRadar service. possible arguments: - search_id (Required): The search ID to delete. Args: client (Client): QRadar client to perform the API call. args (Dict): Demisto args. Returns: CommandResults. """ search_id: str = args.get("search_id", "") # if this call fails, raise an error and stop command execution response = client.search_cancel(search_id) if response.get("status") == "COMPLETED": output = f"Search ID {search_id} is already in a completed status." else: output = f"Search ID {search_id} was successfully cancelled." return CommandResults(readable_output=output, raw_response=response) def qradar_search_results_get_command(client: Client, args: dict) -> CommandResults: """ Retrieves search results from QRadar service. possible arguments: - search_id: Search ID to retrieve its results. - output_path: If specified, will be context output path prefix. - range: Range of offenses to return (e.g.: 0-20, 3-5, 3-3). Args: client (Client): QRadar client to perform the API call. args (Dict): Demisto args. Returns: CommandResults. """ search_id: str = args.get("search_id", "") output_path = args.get("output_path") # Using or instead of default value for QRadarFullSearch backward compatibility range_ = f"""items={args.get("range") or DEFAULT_RANGE_VALUE}""" # if this call fails, raise an error and stop command execution response = client.search_results_get(search_id, range_) if not response: raise DemistoException("Unexpected response from QRadar service.") result_key = list(response.keys())[0] outputs = sanitize_outputs(response.get(result_key)) outputs_prefix = output_path if output_path else f'QRadar.Search(val.ID === "{search_id}").Result.{result_key}' return CommandResults( readable_output=tableToMarkdown(f"Search Results For Search ID {search_id}", outputs), outputs_prefix=outputs_prefix, outputs=outputs, raw_response=response, ) def qradar_reference_sets_list_command(client: Client, args: dict) -> CommandResults: """ Retrieves list of reference sets from QRadar service. possible arguments: - ref_name: Retrieves details of the specific reference that corresponds to the reference name given. - range: Range of offenses to return (e.g.: 0-20, 3-5, 3-3). - filter: Query filter to filter results returned by QRadar service. see https://www.ibm.com/support/knowledgecenter/SS42VS_SHR/com.ibm.qradarapi.doc/c_rest_api_filtering.html for more details. - fields: If used, will filter all fields except for the specified ones. Use this parameter to specify which fields you would like to get back in the response. Fields that are not explicitly named are excluded. Args: client (Client): QRadar client to perform the API call. args (Dict): Demisto args. Returns: CommandResults. """ ref_name = args.get("ref_name") convert_date_value = argToBoolean(args.get("date_value", False)) range_ = f"""items={args.get("range", DEFAULT_RANGE_VALUE)}""" filter_ = args.get("filter") fields = args.get("fields") # if this call fails, raise an error and stop command execution response = client.reference_sets_list(range_, ref_name, filter_, fields) if ref_name: outputs = dict(response) if convert_date_value and outputs.get("element_type") == "DATE": for data_entry in outputs.get("data", []): data_entry["value"] = get_time_parameter(data_entry.get("value"), iso_format=True) outputs["data"] = sanitize_outputs(outputs.get("data", []), REFERENCE_SET_DATA_RAW_FORMATTED) else: outputs = response final_outputs = sanitize_outputs(outputs, REFERENCE_SETS_RAW_FORMATTED) headers = build_headers( ["Name", "ElementType", "Data", "TimeToLive", "TimeoutType"], set(REFERENCE_SETS_RAW_FORMATTED.values()) ) return CommandResults( readable_output=tableToMarkdown("Reference Sets List", final_outputs, headers, removeNull=True), outputs_prefix="QRadar.Reference", outputs_key_field="Name", outputs=final_outputs, raw_response=response, ) def qradar_reference_maps_list_command(client: Client, args: dict) -> CommandResults: """ Retrieves list of reference maps from QRadar service. possible arguments: - ref_name: Retrieves details of the specific reference that corresponds to the reference name given. - range: Range of reference maps to return (e.g.: 0-20, 3-5, 3-3). - filter: Query filter to filter results returned by QRadar service. see https://www.ibm.com/support/knowledgecenter/SS42VS_SHR/com.ibm.qradarapi.doc/c_rest_api_filtering.html for more details. - fields: If used, will filter all fields except for the specified ones. Use this parameter to specify which fields you would like to get back in the response. Fields that are not explicitly named are excluded. Args: client (Client): QRadar client to perform the API call. args (Dict): Demisto args. Returns: CommandResults. """ ref_name = args.get("ref_name") range_ = f"""items={args.get("range", DEFAULT_RANGE_VALUE)}""" filter_ = args.get("filter") fields = args.get("fields") # if this call fails, raise an error and stop command execution response = client.reference_maps_list(range_, ref_name, filter_, fields) if ref_name: outputs = dict(response) # Reformat the data for better context layout, as maps are a dict of dicts data = [{**value, "key": key} for key, value in outputs.get("data", {}).items()] outputs["data"] = sanitize_outputs(data, REFERENCE_MAP_DATA_RAW_FORMATTED) else: outputs = response final_outputs = sanitize_outputs(outputs, REFERENCE_MAPS_RAW_FORMATTED) headers = ["Name", "ElementType", "Namespace", "CollectionID", "TimeoutType", "NumberOfElements", "CreationTime"] readable = tableToMarkdown("Reference Maps List", final_outputs, headers, removeNull=True) if ref_name and final_outputs: readable = f'{readable}\n{tableToMarkdown("Reference Map Data", final_outputs[0].get("Data",[]), removeNull=True)}' return CommandResults( readable_output=readable, outputs_prefix="QRadar.ReferenceMaps", outputs_key_field="Name", outputs=final_outputs, raw_response=response, ) def qradar_reference_set_create_command(client: Client, args: dict) -> CommandResults: """ Create a new reference set. possible arguments: - ref_name (Required): The name of the new reference set. - element_type (Required): The type of the new reference set. Can be ALN (alphanumeric), ALNIC (alphanumeric ignore case), IP (IP address), NUM (numeric), PORT (port number) or DATE. - timeout_type: Indicates if the time_to_live interval is based on when the data was first seen or last seen. The allowed values are 'FIRST_SEEN', 'LAST_SEEN' and 'UNKNOWN'. The default value is 'UNKNOWN'. - time_to_live: The time to live interval, for example: '1 month' or '5 minutes'. - fields: If used, will filter all fields except for the specified ones. Use this parameter to specify which fields you would like to get back in the response. Fields that are not explicitly named are excluded. Args: client (Client): QRadar client to perform the API call. args (Dict): Demisto args. Returns: CommandResults. """ ref_name: str = args.get("ref_name", "") element_type: str = args.get("element_type", "") timeout_type = args.get("timeout_type") time_to_live = args.get("time_to_live") fields = args.get("fields") # if this call fails, raise an error and stop command execution response = client.reference_set_create(ref_name, element_type, timeout_type, time_to_live, fields) outputs = sanitize_outputs(response, REFERENCE_SETS_RAW_FORMATTED) headers = build_headers( ["Name", "ElementType", "Data", "TimeToLive", "TimeoutType"], set(REFERENCE_SETS_RAW_FORMATTED.values()) ) return CommandResults( readable_output=tableToMarkdown("Reference Set Create", outputs, headers, removeNull=True), outputs_prefix="QRadar.Reference", outputs_key_field="Name", outputs=outputs, raw_response=response, ) def qradar_reference_set_delete_command(client: Client, args: dict) -> CommandResults: """ Removes a reference set or purges its contents. possible arguments: - ref_name (Required): The name of the new reference set. - purge_only: Indicates if the reference set should have its contents purged (true), keeping the reference set structure. If the value is 'false', or not specified the reference set is removed completely. Default is 'false'. - fields: If used, will filter all fields except for the specified ones. Use this parameter to specify which fields you would like to get back in the response. Fields that are not explicitly named are excluded. Args: client (Client): QRadar client to perform the API call. args (Dict): Demisto args. Returns: CommandResults. """ ref_name: str = args.get("ref_name", "") purge_only = args.get("purge_only") fields = args.get("fields") # if this call fails, raise an error and stop command execution response = client.reference_set_delete(ref_name, purge_only, fields) return CommandResults( raw_response=response, readable_output=f"Request to delete reference {ref_name} was submitted." f""" Current deletion status: {response.get("status", "Unknown")}""", ) @polling_function(name="qradar-reference-set-value-upsert", requires_polling_arg=False) def qradar_reference_set_value_upsert_command(args: dict, client: Client, params: dict) -> PollResult: """ Update or insert new value to a reference set from QRadar service. possible arguments: - ref_name (Required): The reference name to insert/update a value for. - values (Required): Comma separated list. All the values to be inserted/updated. - source: An indication of where the data originated. Default is reference data api. - date_value: Boolean, specifies if values given are dates or not. - range: Range of offenses to return (e.g.: 0-20, 3-5, 3-3). - fields: If used, will filter all fields except for the specified ones. Use this parameter to specify which fields you would like to get back in the response. Fields that are not explicitly named are excluded. Args: client (Client): QRadar client to perform the API call. args (Dict): Demisto args. Returns: PollResult. """ return insert_values_to_reference_set_polling( client, params.get("api_version", ""), args, values=argToList(args.get("value", "")) ) def qradar_reference_map_value_upsert_command(client: Client, args: dict[str, Any]) -> CommandResults: """ Update or insert new value to a reference map from QRadar service. possible arguments: - ref_name (Required): The name of the reference map to insert/update a value for. - value (Required): The value to be inserted/updated. - key: The key to update. - source: An indication of where the data originated. Default is reference data api. Args: client (Client): QRadar client to perform the API call. args (Dict): Demisto args. Returns: CommandResults. """ ref_name: str = args.get("ref_name", "") if not ref_name: raise DemistoException("ref_name must not be empty") value: str = args.get("value", "") key: str = args.get("key", "") source: str | None = args.get("source") if not value or not key: raise DemistoException("key and value must not be empty") try: response = client.reference_map_value_upsert(ref_name, key, value, source) return CommandResults( readable_output=f'Reference map "{ref_name}" successfully updated: "{key}" set to "{value}".', raw_response=response, ) except DemistoException as ex: return CommandResults( readable_output=f"Failed to insert or update {key} in {ref_name}: {ex.message}", entry_type=EntryType.ERROR ) def qradar_reference_set_value_delete_command(client: Client, args: dict) -> CommandResults: """ Delete a value in reference set from QRadar service. possible arguments: - ref_name (Required): The reference name to insert/update a value for. - value (Required): Value to be deleted. - date_value: Boolean, specifies if values given are dates or not. Args: client (Client): QRadar client to perform the API call. args (Dict): Demisto args. Returns: CommandResults. """ ref_name: str = args.get("ref_name", "") value: str = args.get("value", "") date_value = argToBoolean(args.get("date_value", False)) original_value = value if date_value: value = str(get_time_parameter(original_value, epoch_format=True)) # if this call fails, raise an error and stop command execution try: response = client.reference_set_value_delete(ref_name, value) except DemistoException as e: response = str(e) if f"Set {ref_name} does not contain value {value}" not in response: raise e human_readable = f"### value: {original_value} of reference: {ref_name} was deleted successfully" return CommandResults(readable_output=human_readable, raw_response=response) def qradar_domains_list_command(client: Client, args: dict) -> CommandResults: """ Retrieves list of domains sets from QRadar service. If you do not have the System Administrator or Security Administrator permissions, then for each domain assigned to your security profile you can only view the values for the id and name fields. All other values return null. possible arguments: - domain_id: Retrieves details of the specific domain that corresponds to the ID given. - range: Range of offenses to return (e.g.: 0-20, 3-5, 3-3). - filter: Query filter to filter results returned by QRadar service. see https://www.ibm.com/support/knowledgecenter/SS42VS_SHR/com.ibm.qradarapi.doc/c_rest_api_filtering.html for more details. - fields: If used, will filter all fields except for the specified ones. Use this parameter to specify which fields you would like to get back in the response. Fields that are not explicitly named are excluded. Args: client (Client): QRadar client to perform the API call. args (Dict): Demisto args. Returns: CommandResults. """ # backward compatibility for domain_id argument named is 'id' in QRadar v2. domain_id = args.get("domain_id") or args.get("id") range_ = f"""items={args.get("range", DEFAULT_RANGE_VALUE)}""" filter_ = args.get("filter") fields = args.get("fields") # if this call fails, raise an error and stop command execution response = client.domains_list(domain_id, range_, filter_, fields) outputs = sanitize_outputs(response, DOMAIN_RAW_FORMATTED) return CommandResults( readable_output=tableToMarkdown("Domains List", outputs, removeNull=True), outputs_prefix="QRadar.Domains", outputs_key_field="ID", outputs=outputs, raw_response=response, ) @polling_function(name="qradar-indicators-upload", requires_polling_arg=False) def qradar_indicators_upload_command(args: dict, client: Client, params: dict) -> PollResult: """ Uploads list of indicators from Demisto to a reference set in QRadar service. possible arguments: - ref_name (Required): Name of the reference set to upload indicators to. - query: The query for getting indicators from Demisto. - limit: Maximum number of indicators to fetch from Demisto. - page: The page from which to get the indicators. - fields: If used, will filter all fields except for the specified ones. Use this parameter to specify which fields you would like to get back in the response. Fields that are not explicitly named are excluded. Args: client (Client): QRadar client to perform the API call. args (Dict): Demisto args. Returns: PollResult. """ return insert_values_to_reference_set_polling(client, params.get("api_version", ""), args, from_indicators=True) def flatten_nested_geolocation_values(geolocation_dict: dict, dict_key: str, nested_value_keys: List[str]) -> dict: """ Receives output from geolocation IPs command, and does: 1) flattens output, takes nested keys values. 2) Converts keys to prefix of 'dict_key' and suffix of nested key as camel case. Args: geolocation_dict (Dict): The dict to flatten. dict_key (Dict): The key of the inner dict to use his values. nested_value_keys (Dict): The keys inside inner dict to take. Returns: (Dict): dict of ({dict_key_name}{camel case nested key}: {nested key value} """ return { f"{camelize_string(dict_key)}{camelize_string(k)}": geolocation_dict.get(dict_key, {}).get(k) for k in nested_value_keys } def qradar_geolocations_for_ip_command(client: Client, args: dict) -> CommandResults: """ Retrieves the MaxMind geoip data for the given IP addresses. possible arguments: - ip (Required): Comma separated list. the IPs to retrieve data for. - fields: If used, will filter all fields except for the specified ones. Use this parameter to specify which fields you would like to get back in the response. Fields that are not explicitly named are excluded. Args: client (Client): QRadar client to perform the API call. args (Dict): Demisto args. Returns: CommandResults. """ ips = argToList(args.get("ip")) filter_ = f"""ip_address IN ({",".join((f'"{ip!s}"' for ip in ips))})""" # noqa: UP034 fields = args.get("fields") # if this call fails, raise an error and stop command execution response = client.geolocations_for_ip(filter_, fields) outputs = [] for output in response: city_values = flatten_nested_geolocation_values(output, "city", ["name"]) continent_values = flatten_nested_geolocation_values(output, "continent", ["name"]) location_values = flatten_nested_geolocation_values( output, "location", ["accuracy_radius", "average_income", "latitude", "longitude", "metro_code", "population_density", "timezone"], ) physical_country_values = flatten_nested_geolocation_values(output, "physical_country", ["iso_code", "name"]) registered_country_values = flatten_nested_geolocation_values(output, "registered_country", ["iso_code", "name"]) represented_country_values = flatten_nested_geolocation_values( output, "represented_country", ["iso_code", "name", "confidence"] ) subdivision_values = flatten_nested_geolocation_values(output, "subdivision", ["name", "iso_code", "confidence"]) non_nested_values = { "IPAddress": output.get("ip_address"), "Traits": output.get("traits"), "Coordinates": output.get("geo_json", {}).get("coordinates"), "PostalCode": output.get("postal", {}).get("postal_code"), "PostalCodeConfidence": output.get("postal", {}).get("confidence"), } final_output = dict( city_values, **continent_values, **location_values, **physical_country_values, **registered_country_values, **represented_country_values, **subdivision_values, **non_nested_values, ) outputs.append(final_output) final_outputs = sanitize_outputs(outputs) return CommandResults( readable_output=tableToMarkdown("Geolocation For IP", final_outputs), outputs_prefix="QRadar.GeoForIP", outputs_key_field="IPAddress", outputs=final_outputs, raw_response=response, ) def qradar_log_sources_list_command(client: Client, args: dict) -> CommandResults: """ Retrieves a list of log sources from QRadar service. possible arguments: - qrd_encryption_algorithm: The algorithm to use for encrypting the sensitive data of this endpoint. Using AES 128 - qrd_encryption_password: The password to use for encrypting the sensitive data of this endpoint. If argument was not given, will be randomly generated. - range: Range of offenses to return (e.g.: 0-20, 3-5, 3-3). - filter: Query filter to filter results returned by QRadar service. see https://www.ibm.com/support/knowledgecenter/SS42VS_SHR/com.ibm.qradarapi.doc/c_rest_api_filtering.html for more details. - fields: If used, will filter all fields except for the specified ones. Use this parameter to specify which fields you would like to get back in the response. Fields that are not explicitly named are excluded. - id: If used, will fetch only the specified log source. Args: client (Client): QRadar client to perform the API call. args (Dict): Demisto args. Returns: CommandResults. """ qrd_encryption_algorithm: str = args.get("qrd_encryption_algorithm", "AES128") qrd_encryption_password: str = args.get("qrd_encryption_password", secrets.token_urlsafe(20)) endpoint = "/config/event_sources/log_source_management/log_sources" range_ = f"""items={args.get("range", DEFAULT_RANGE_VALUE)}""" filter_ = args.get("filter") fields = args.get("fields") additional_headers = { "x-qrd-encryption-algorithm": qrd_encryption_algorithm, "x-qrd-encryption-password": qrd_encryption_password, } id = args.get("id") # if this call fails, raise an error and stop command execution response = client.get_resource(id, range_, endpoint, filter_, fields, additional_headers) outputs = sanitize_outputs(response, LOG_SOURCES_RAW_FORMATTED) readable_outputs = [{k: v for k, v in output.items() if k != "ProtocolParameters"} for output in outputs] headers = build_headers(["ID", "Name", "Description"], set(LOG_SOURCES_RAW_FORMATTED.values())) return CommandResults( readable_output=tableToMarkdown("Log Sources List", readable_outputs, headers, removeNull=True), outputs_prefix="QRadar.LogSource", outputs_key_field="ID", outputs=outputs, raw_response=response, ) def qradar_get_custom_properties_command(client: Client, args: dict) -> CommandResults: """ Retrieves a list of event regex properties from QRadar service. possible arguments: - field_names: A comma-separated list of names of an exact properties to search for. - range: Range of offenses to return (e.g.: 0-20, 3-5, 3-3). - filter: Query filter to filter results returned by QRadar service. see https://www.ibm.com/support/knowledgecenter/SS42VS_SHR/com.ibm.qradarapi.doc/c_rest_api_filtering.html for more details. - fields: If used, will filter all fields except for the specified ones. Use this parameter to specify which fields you would like to get back in the response. Fields that are not explicitly named are excluded. Args: client (Client): QRadar client to perform the API call. args (Dict): Demisto args. Returns: CommandResults. """ limit = arg_to_number(args.get("limit", DEFAULT_LIMIT_VALUE)) range_ = f"items=0-{limit - 1}" if limit else f"items={args.get('range', DEFAULT_RANGE_VALUE)}" like_names = argToList(args.get("like_name")) field_names = argToList(args.get("field_name")) filter_ = args.get("filter", "") fields = args.get("fields") if not filter_: if field_names: filter_ += f"""name IN ({",".join(f'"{name!s}"' for name in field_names)})""" if like_names: filter_ += " or ".join(f' name ILIKE "%{like}%"' for like in like_names) # if this call fails, raise an error and stop command execution response = client.custom_properties(range_, filter_, fields) outputs = sanitize_outputs(response) return CommandResults( readable_output=tableToMarkdown("Custom Properties", outputs, removeNull=True), outputs_prefix="QRadar.Properties", outputs_key_field="identifier", outputs=outputs, raw_response=response, ) def perform_ips_command_request(client: Client, args: dict[str, Any], is_destination_addresses: bool): """ Performs request to QRadar IPs endpoint. Args: client (Client): Client to perform the request to QRadar service. args (Dict[str, Any]): XSOAR arguments. is_destination_addresses (bool): Whether request is for destination addresses or source addresses. Returns: - Request response. """ range_: str = f"""items={args.get("range", DEFAULT_RANGE_VALUE)}""" filter_: Optional[str] = args.get("filter") fields: Optional[str] = args.get("fields") address_type = "local_destination" if is_destination_addresses else "source" ips_arg_name: str = f"{address_type}_ip" ips: List[str] = argToList(args.get(ips_arg_name, [])) if ips and filter_: raise DemistoException(f"Both filter and {ips_arg_name} have been supplied. Please supply only one.") if ips: filter_ = " OR ".join([f'{ips_arg_name}="{ip_}"' for ip_ in ips]) url_suffix = f"{address_type}_addresses" # if this call fails, raise an error and stop command execution response = client.get_addresses(url_suffix, filter_, fields, range_) return response def qradar_ips_source_get_command(client: Client, args: dict[str, Any]) -> CommandResults: """ Get source IPS from QRadar service. Args: client (Client): Client to perform API calls to QRadar service. args (Dict[str, Any): XSOAR arguments. Returns: (CommandResults). """ response = perform_ips_command_request(client, args, is_destination_addresses=False) outputs = sanitize_outputs(response, SOURCE_IPS_RAW_FORMATTED) return CommandResults( readable_output=tableToMarkdown("Source IPs", outputs), outputs_prefix="QRadar.SourceIP", outputs_key_field="ID", outputs=outputs, raw_response=response, ) def qradar_ips_local_destination_get_command(client: Client, args: dict[str, Any]) -> CommandResults: """ Get local destination IPS from QRadar service. Args: client (Client): Client to perform API calls to QRadar service. args (Dict[str, Any): XSOAR arguments. Returns: (CommandResults). """ response = perform_ips_command_request(client, args, is_destination_addresses=True) outputs = sanitize_outputs(response, LOCAL_DESTINATION_IPS_RAW_FORMATTED) return CommandResults( readable_output=tableToMarkdown("Local Destination IPs", outputs), outputs_prefix="QRadar.LocalDestinationIP", outputs_key_field="ID", outputs=outputs, raw_response=response, ) def qradar_reset_last_run_command() -> str: """ Puts the reset flag inside integration context in a concurrency-safe way. Returns: (str): 'fetch-incidents was reset successfully'. """ # Set RESET_KEY to True to identify whether reset was triggered during long running command execution partial_changes = {RESET_KEY: True} safely_update_context_data_partial(partial_changes) return "fetch-incidents was reset successfully." def qradar_get_mapping_fields_command(client: Client) -> dict: """ Returns Dict object containing the list of fields for an incident type. This command should be used for debugging purposes. Args: client (Client): Client to perform API calls. Returns: (Dict): Contains all the mapping. """ offense = { "username_count": "int", "description": "str", "rules": {"id": "int", "type": "str", "name": "str"}, "event_count": "int", "flow_count": "int", "assigned_to": "NoneType", "security_category_count": "int", "follow_up": "bool", "source_address_ids": "str", "source_count": "int", "inactive": "bool", "protected": "bool", "closing_user": "str", "destination_networks": "str", "source_network": "str", "category_count": "int", "close_time": "str", "remote_destination_count": "int", "start_time": "str", "magnitude": "int", "last_updated_time": "str", "credibility": "int", "id": "int", "categories": "str", "severity": "int", "policy_category_count": "int", "closing_reason_id": "str", "device_count": "int", "offense_type": "str", "relevance": "int", "domain_id": "int", "offense_source": "str", "local_destination_address_ids": "int", "local_destination_count": "int", "status": "str", "domain_name": "str", } events = { "events": { "qidname_qid": "str", "logsourcename_logsourceid": "str", "categoryname_highlevelcategory": "str", "categoryname_category": "str", "protocolname_protocolid": "str", "sourceip": "str", "sourceport": "int", "destinationip": "str", "destinationport": "int", "qiddescription_qid": "str", "username": "NoneType", "rulename_creeventlist": "str", "sourcegeographiclocation": "str", "sourceMAC": "str", "sourcev6": "str", "destinationgeographiclocation": "str", "destinationv6": "str", "logsourcetypename_devicetype": "str", "credibility": "int", "severity": "int", "magnitude": "int", "eventcount": "int", "eventDirection": "str", "postNatDestinationIP": "str", "postNatDestinationPort": "int", "postNatSourceIP": "str", "postNatSourcePort": "int", "preNatDestinationPort": "int", "preNatSourceIP": "str", "preNatSourcePort": "int", "utf8_payload": "str", "starttime": "str", "devicetime": "int", } } assets = { "assets": { "interfaces": { "mac_address": "str", "ip_addresses": {"type": "str", "value": "str"}, "id": "int", "Unified Name": "str", "Technical User": "str", "Switch ID": "str", "Business Contact": "str", "CVSS Availability Requirement": "str", "Compliance Notes": "str", "Primary OS ID": "str", "Compliance Plan": "str", "Switch Port ID": "str", "Weight": "str", "Location": "str", "CVSS Confidentiality Requirement": "str", "Technical Contact": "str", "Technical Owner": "str", "CVSS Collateral Damage Potential": "str", "Description": "str", "Business Owner": "str", "CVSS Integrity Requirement": "str", }, "id": "int", "domain_id": "int", "domain_name": "str", } } # if this call fails, raise an error and stop command execution custom_fields = { "events": { field.get("name"): field.get("property_type") for field in client.custom_properties() if "name" in field and "property_type" in field } } fields = { "Offense": offense, "Events: Builtin Fields": events, "Events: Custom Fields": custom_fields, "Assets": assets, } return fields def update_events_mirror_message( mirror_options: Optional[Any], events_limit: int, events_count: int, events_mirrored: int, events_mirrored_collapsed: int, fetch_mode: str, offense_id: int, failure_message: Optional[str] = None, ) -> str: """Return the offense's events' mirror error message. Args: mirror_options (str): The mirror options for the instance. events_limit (int): The events limit for the mirroring. failure_message (str): A failure message if there was a failure during fetching of events. events_count (int): The number of events in the offense. events_mirrored (int): The number of events mirrored in the offense Returns: (str) An updated offense events mirror message. """ mirroring_events_message = "Unknown" print_debug_msg( f"Events status for Offense {offense_id}:\n" f"mirror_options {mirror_options}\n events_limit {events_limit} \n" f"failure_message {failure_message}\n events_count {events_count}\n " f"events_mirrored {events_mirrored}" ) if mirror_options != MIRROR_OFFENSE_AND_EVENTS: mirroring_events_message = "" elif failure_message: mirroring_events_message = failure_message elif fetch_mode == FetchMode.all_events.value and events_mirrored < min(events_count, events_limit): mirroring_events_message = "Fetching events did not get all events of the offense" elif events_mirrored == events_count: mirroring_events_message = "All available events in the offense were fetched." elif events_mirrored_collapsed == events_limit: mirroring_events_message = "Fetching events has reached events limit in this incident." return mirroring_events_message def get_remote_data_command(client: Client, params: dict[str, Any], args: dict) -> GetRemoteDataResponse: """ get-remote-data command: Returns an updated incident and entries If offense's events were updated in the long running container, update the demisto incident. Args: client (Client): QRadar client to perform the API calls. params (Dict): Demisto params. args (Dict): id: Offense id to retrieve. lastUpdate: When was the last time we data was retrieved in Epoch. Returns: GetRemoteDataResponse. """ remote_args = GetRemoteDataArgs(args) ip_enrich, asset_enrich = get_offense_enrichment(params.get("enrichment", "IPs And Assets")) offense_id = str(remote_args.remote_incident_id) print_debug_msg(f"Starting get-remote-data for offense {offense_id}") # if this call fails, raise an error and stop command execution offense = client.offenses_list(offense_id=int(offense_id)) offense_last_update = get_time_parameter(offense.get("last_persisted_time")) mirror_options = params.get("mirror_options") context_data = qradar_get_integration_context() events_columns = params.get("events_columns") or DEFAULT_EVENTS_COLUMNS events_limit = int(params.get("events_limit") or DEFAULT_EVENTS_LIMIT) fetch_mode = params.get("fetch_mode", "") print_context_data_stats(context_data, f"Starting Get Remote Data For Offense {offense.get('id')!s}") demisto.debug(f"Updating offense. Offense last update was {offense_last_update}") entries = [] if offense.get("status") == "CLOSED" and argToBoolean(params.get("close_incident", False)): demisto.debug(f"Offense {offense_id} is closed: {offense}.") try: if closing_reason := offense.get("closing_reason_id", ""): closing_reason = client.closing_reasons_list(closing_reason).get("text") offense_close_time = offense.get("close_time", "") closed_offense_notes = client.offense_notes_list( int(offense_id), f"items={DEFAULT_RANGE_VALUE}", filter_=f"create_time >= {offense_close_time}" ) # In QRadar UI, when you close a reason, a note is added with the reason and more details. Try to get note # if exists, else fallback to closing reason only, as closing QRadar through an API call does not create a note. closenotes = next( ( note.get("note_text") for note in closed_offense_notes if note.get("note_text").startswith("This offense was closed with reason:") ), closing_reason, ) if not closing_reason: print_debug_msg(f"Could not find closing reason or closing note for offense with offense id {offense_id}") closing_reason = "Unknown closing reason from QRadar" closenotes = "Unknown closing note from QRadar" except Exception as e: demisto.error(f"Failed to get closing reason with error: {e}") closing_reason = "Unknown closing reason from QRadar" closenotes = "Unknown closing note from QRadar" time.sleep(FAILURE_SLEEP) entries.append( { "Type": EntryType.NOTE, "Contents": { "dbotIncidentClose": True, "closeReason": closing_reason, "closeNotes": f"From QRadar: {closenotes}", }, "ContentsFormat": EntryFormat.JSON, } ) already_mirrored = False if mirror_options == MIRROR_OFFENSE_AND_EVENTS: if (num_events := context_data.get(MIRRORED_OFFENSES_FETCHED_CTX_KEY, {}).get(offense_id)) and int(num_events) >= ( events_limit := int(params.get("events_limit", DEFAULT_EVENTS_LIMIT)) ): print_debug_msg( f"Events were already fetched {num_events} for offense {offense_id}, " f"and are more than the events limit, {events_limit}. " f"Not fetching events again." ) # delete the offense from the queue delete_offense_from_context(offense_id) already_mirrored = True else: events, status = get_remote_events( client, offense_id, context_data, events_columns, events_limit, fetch_mode, ) print_context_data_stats(context_data, f"Get Remote Data events End for id {offense_id}") if status != QueryStatus.SUCCESS.value: # we raise an exception because we don't want to change the offense until all events are fetched. print_debug_msg(f"Events not mirrored yet for offense {offense_id}. Status: {status}") raise DemistoException(f"Events not mirrored yet for offense {offense_id}") offense["events"] = events enriched_offense = enrich_offenses_result(client, offense, ip_enrich, asset_enrich) final_offense_data = sanitize_outputs(enriched_offense)[0] if not already_mirrored: events_mirrored = get_num_events(final_offense_data.get("events", [])) print_debug_msg(f"Offense {offense_id} mirrored events: {events_mirrored}") events_message = update_events_mirror_message( mirror_options=mirror_options, events_limit=events_limit, events_count=int(final_offense_data.get("event_count", 0)), events_mirrored=events_mirrored, events_mirrored_collapsed=len(final_offense_data.get("events", [])), fetch_mode=fetch_mode, offense_id=int(offense_id), ) print_debug_msg(f"offense {offense_id} events_message: {events_message}") final_offense_data["last_mirror_in_time"] = datetime.now().isoformat() final_offense_data["mirroring_events_message"] = events_message final_offense_data["events_fetched"] = events_mirrored return GetRemoteDataResponse(final_offense_data, entries) def add_modified_remote_offenses( client: Client, context_data: dict, mirror_options: str, new_modified_records_ids: set[str], new_last_update_modified: int, new_last_update_closed: int, events_columns: str, events_limit: int, fetch_mode: str, ) -> set: """Add modified remote offenses to context_data and handle exhausted offenses. Args: client: Qradar client context_data: The context data to update. mirror_options: The mirror options for the integration. new_modified_records_ids: The new modified offenses ids. new_last_update_modified: The current last mirror update modified. new_last_update_closed: The current last mirror update. events_columns: The events_columns param. events_limit: The events_limit param. Returns: The new modified records ids """ # We'll keep local references to the relevant sub-dicts, just as before: mirrored_offenses_queries = context_data.get(MIRRORED_OFFENSES_QUERIED_CTX_KEY, {}) finished_offenses_queue = context_data.get(MIRRORED_OFFENSES_FINISHED_CTX_KEY, {}) changed_ids_ctx = [] if mirror_options == MIRROR_OFFENSE_AND_EVENTS: print_context_data_stats(context_data, "Get Modified Remote Data - Before update") current_concurrent_searches = get_current_concurrent_searches(context_data) offense_ids_to_search = [] # Move completed queries from 'queried' to 'finished' or mark them 'ERROR' for offense_id, search_id in mirrored_offenses_queries.copy().items(): if search_id in {QueryStatus.WAIT.value, QueryStatus.ERROR.value}: # re-submit search offense_ids_to_search.append(offense_id) continue # see if the existing search completed _, status = poll_offense_events(client, search_id, should_get_events=False, offense_id=int(offense_id)) if status == QueryStatus.ERROR.value: time.sleep(FAILURE_SLEEP) print_debug_msg(f"offense {offense_id}, search query {search_id}, status is {status}") mirrored_offenses_queries[offense_id] = QueryStatus.ERROR.value current_concurrent_searches -= 1 elif status == QueryStatus.SUCCESS.value: del mirrored_offenses_queries[offense_id] finished_offenses_queue[offense_id] = search_id # add the offense id to modified in order to run get_remote_data new_modified_records_ids.add(offense_id) changed_ids_ctx.append(offense_id) current_concurrent_searches -= 1 else: print_debug_msg(f"offense {offense_id}, search query {search_id}, status is {status}") # Create new search for any WAIT/ERROR offense if concurrency limit not reached for offense_id in offense_ids_to_search: if current_concurrent_searches >= MAX_SEARCHES_QUEUE: print_debug_msg(f"Reached maximum concurrent searches ({MAX_SEARCHES_QUEUE}), will try again later.") break current_concurrent_searches += 1 new_search_id = create_events_search(client, fetch_mode, events_columns, events_limit, int(offense_id)) mirrored_offenses_queries[offense_id] = new_search_id changed_ids_ctx.append(offense_id) # Build partial_changes dict with only the keys we want to write partial_changes = { LAST_MIRROR_KEY: new_last_update_modified, LAST_MIRROR_CLOSED_KEY: new_last_update_closed, } # If we are in "Mirror Offense & Events" mode, also update the queries/finished sub-dicts if mirror_options == MIRROR_OFFENSE_AND_EVENTS: partial_changes[MIRRORED_OFFENSES_QUERIED_CTX_KEY] = mirrored_offenses_queries partial_changes[MIRRORED_OFFENSES_FINISHED_CTX_KEY] = finished_offenses_queue # Now safely merge these partial changes. safely_update_context_data_partial( partial_changes, override_keys=[MIRRORED_OFFENSES_QUERIED_CTX_KEY, MIRRORED_OFFENSES_FINISHED_CTX_KEY], ) # Do final logging for debugging if desired print_context_data_stats(context_data, "Get Modified Remote Data - After update") return new_modified_records_ids def create_events_search( client: Client, fetch_mode: str, events_columns: str, events_limit: int, offense_id: int, offense_start_time: str | None = None, return_raw_response: bool = False, ) -> str: additional_where = "" if fetch_mode == FetchMode.correlations_events_only.value: additional_where = """ AND LOGSOURCETYPENAME(devicetype) = 'Custom Rule Engine' """ try: # Get all the events starting from one hour after epoch if not offense_start_time: offense = client.offenses_list(offense_id=offense_id) offense_start_time = offense["start_time"] query_expression = ( f"SELECT {events_columns} FROM events WHERE INOFFENSE({offense_id}) {additional_where} limit {events_limit} " # noqa: S608, E501 f"START {offense_start_time}" ) print_debug_msg(f"Creating search for offense ID: {offense_id}, query_expression: {query_expression}") search_response = client.search_create(query_expression) print_debug_msg( f"Created search for offense ID: {offense_id}, " f"Start Time: {offense_start_time}, " f"events_limit: {events_limit}, " f"ret_value: {search_response}." ) if return_raw_response: return search_response return search_response["search_id"] if search_response["search_id"] else QueryStatus.ERROR.value except Exception as e: print_debug_msg(f"Search for {offense_id} failed. Error: {e}") time.sleep(FAILURE_SLEEP) return QueryStatus.ERROR.value def get_modified_remote_data_command( client: Client, params: dict[str, str], args: dict[str, str] ) -> GetModifiedRemoteDataResponse: """ Performs API calls to QRadar service, querying for offenses that were updated in QRadar later than the last update time given in the argument 'lastUpdate'. Args: client (Client): QRadar client to perform the API calls. params (Dict): Demisto params. args (Dict): Demisto arguments. Returns: (GetModifiedRemoteDataResponse): IDs of the offenses that have been modified in QRadar. """ ctx = qradar_get_integration_context() remote_args = GetModifiedRemoteDataArgs(args) highest_fetched_id = ctx.get(LAST_FETCH_KEY, 0) limit: int = int(params.get("mirror_limit", MAXIMUM_MIRROR_LIMIT)) fetch_mode = params.get("fetch_mode", "") range_ = f"items=0-{limit - 1}" last_update_modified = ctx.get(LAST_MIRROR_KEY, 0) if not last_update_modified: # This is the first mirror. We get the last update of the latest incident with a window of 5 minutes last_update = dateparser.parse(remote_args.last_update) if not last_update: last_update = datetime.now() last_update -= timedelta(minutes=5) last_update_modified = int(last_update.timestamp() * 1000) last_update_closed = ctx.get(LAST_MIRROR_CLOSED_KEY, last_update_modified) assert isinstance(last_update_modified, int) assert isinstance(last_update_closed, int) filter_modified = f"id <= {highest_fetched_id} AND status!=closed AND last_persisted_time > {last_update_modified}" filter_closed = f"id <= {highest_fetched_id} AND status=closed AND close_time > {last_update_closed}" print_debug_msg(f"Filter to get modified offenses is: {filter_modified}") print_debug_msg(f"Filter to get closed offenses is: {filter_closed}") # if this call fails, raise an error and stop command execution offenses_modified = client.offenses_list( range_=range_, filter_=filter_modified, sort="+last_persisted_time", fields=FIELDS_MIRRORING ) offenses_closed = client.offenses_list(range_=range_, filter_=filter_closed, sort="+close_time", fields=FIELDS_MIRRORING) if offenses_modified: last_update_modified = int(offenses_modified[-1].get("last_persisted_time")) if offenses_closed: last_update_closed = int(offenses_closed[-1].get("close_time")) new_modified_records_ids = {str(offense.get("id")) for offense in offenses_modified + offenses_closed if "id" in offense} print_debug_msg(f"Last update modified: {last_update_modified}, Last update closed: {last_update_closed}") events_columns = params.get("events_columns") or DEFAULT_EVENTS_COLUMNS events_limit = int(params.get("events_limit") or DEFAULT_EVENTS_LIMIT) new_modified_records_ids = add_modified_remote_offenses( client=client, context_data=ctx, mirror_options=params.get("mirror_options", ""), new_modified_records_ids=new_modified_records_ids, new_last_update_modified=last_update_modified, new_last_update_closed=last_update_closed, events_columns=events_columns, events_limit=events_limit, fetch_mode=fetch_mode, ) return GetModifiedRemoteDataResponse(list(new_modified_records_ids)) def qradar_search_retrieve_events_command( client: Client, params, args, ) -> CommandResults: # pragma: no cover (tested in test-playbook) """A polling command to get events from QRadar offense Args: client (Client): The QRadar client to use. params (dict): Parameters passed to the command. args (dict): Demisto arguments. Raises: DemistoException: If the search failed. Returns: CommandResults: The results of the command. """ interval_in_secs = int(args.get("interval_in_seconds", 30)) search_id = args.get("search_id") is_polling = argToBoolean(args.get("polling", True)) timeout_in_secs = int(args.get("timeout_in_seconds", 600)) search_command_results = None if not search_id: search_command_results = qradar_search_create_command(client, params, args) search_id = search_command_results.outputs[0].get("ID") # type: ignore calling_context = demisto.callingContext.get("context", {}) sm = get_schedule_metadata(context=calling_context) end_date: datetime | None = dateparser.parse(sm.get("end_date")) if not end_date or end_date.year == 1: end_date = None # determine if this is the last run of the polling command is_last_run = ( (datetime.now() + timedelta(seconds=interval_in_secs)).timestamp() >= end_date.timestamp() if end_date else False ) try: events, status = poll_offense_events(client, search_id, should_get_events=True, offense_id=args.get("offense_id")) except (DemistoException, requests.Timeout) as e: if is_last_run: raise e print_debug_msg(f"Polling event failed due to {e}. Will try to poll again in the next interval.") events = [] status = QueryStatus.WAIT.value if is_last_run and status == QueryStatus.WAIT.value: print_debug_msg("Its the last run of the polling, will cancel the query request. ") client.search_cancel(search_id=search_id) return CommandResults( readable_output="Got polling timeout. Quary got cancelled.", ) if is_last_run and args.get("success") and not events: # if last run, we want to get the events that were fetched in the previous calls return CommandResults( readable_output="Not all events were fetched. partial data is available.", ) if status == QueryStatus.ERROR.value: raise DemistoException("Polling for events failed") if status == QueryStatus.SUCCESS.value: # return the result only if the all events were retrieved, unless for the last call for this function offense_id = args.get("offense_id", "") events_limit = int(args.get("events_limit", params.get("events_limit"))) fetch_mode: FetchMode = args.get("fetch_mode", params.get("fetch_mode")) if ( argToBoolean(args.get("retry_if_not_all_fetched", False)) and not is_last_run and not is_all_events_fetched(client, fetch_mode, offense_id, events_limit, events) ): # return scheduled command result without search id to search again polling_args = { "interval_in_seconds": interval_in_secs, "timeout_in_seconds": timeout_in_secs, "success": True, **args, } scheduled_command = ScheduledCommand( command="qradar-search-retrieve-events", next_run_in_seconds=interval_in_secs, args=polling_args, timeout_in_seconds=timeout_in_secs, ) return CommandResults( scheduled_command=scheduled_command if is_polling else None, readable_output="Not all events were fetched. Searching again.", outputs_prefix="QRadar.SearchEvents", outputs_key_field="ID", outputs={"Events": events, "ID": search_id, "Status": QueryStatus.PARTIAL}, ) return CommandResults( outputs_prefix="QRadar.SearchEvents", outputs_key_field="ID", outputs={"Events": events, "ID": search_id, "Status": QueryStatus.SUCCESS}, readable_output=tableToMarkdown( f"{get_num_events(events)} Events returned from search_id {search_id}", events, ), ) print_debug_msg(f"Still polling for search results for search ID: {search_id}.") polling_args = { "search_id": search_id, "interval_in_seconds": interval_in_secs, "timeout_in_seconds": timeout_in_secs, **args, } scheduled_command = ScheduledCommand( command="qradar-search-retrieve-events", next_run_in_seconds=interval_in_secs, timeout_in_seconds=timeout_in_secs, args=polling_args, ) outputs = {"ID": search_id, "Status": QueryStatus.WAIT} return CommandResults( scheduled_command=scheduled_command if is_polling else None, readable_output=f"Search ID: {search_id}", outputs_prefix="QRadar.SearchEvents", outputs_key_field="ID", outputs=outputs, ) def qradar_remote_network_cidr_create_command(client: Client, args) -> CommandResults: """Create remote network cidrs Args: client (Client): The QRadar client to use. args (dict): Demisto arguments. Raises: DemistoException: If the args are not valid. Returns: CommandResults. """ cidrs_list = argToList(args.get("cidrs")) cidrs_from_query = get_cidrs_indicators(args.get("query")) name = args.get("name") description = args.get("description") group = args.get("group") fields = args.get("fields") error_message = verify_args_for_remote_network_cidr(cidrs_list, cidrs_from_query, name, group, fields) if error_message: raise DemistoException(error_message) body = {"name": name, "description": description, "cidrs": cidrs_list or cidrs_from_query, "group": group} response = client.create_and_update_remote_network_cidr(body, fields) success_message = "The new staged remote network was successfully created." return CommandResults(raw_response=response, readable_output=tableToMarkdown(success_message, response)) def qradar_remote_network_cidr_list_command(client: Client, args: dict[str, Any]) -> CommandResults: """ Args: client (Client): The QRadar client to use. args (dict): Demisto arguments. Raises: DemistoException: If given both filter and group, id or name arguments. Returns: CommandResults. """ limit = arg_to_number(args.get("limit")) page = arg_to_number(args.get("page")) page_size = arg_to_number(args.get("page_size")) group = args.get("group") id_ = args.get("id") name = args.get("name") filter_ = args.get("filter", "") fields = args.get("fields") error_message = verify_args_for_remote_network_cidr_list(limit, page, page_size, filter_, group, id_, name) if error_message: raise DemistoException(error_message) if page and page_size: first_item = (int(page) - 1) * int(page_size) last_item = int(page) * int(page_size) - 1 range_ = f"items={first_item}-{last_item}" else: range_ = f"items=0-{str(limit - 1) if limit else str(DEFAULT_LIMIT_VALUE - 1)}" if not filter_: if group: filter_ += f'group="{group}"' if id_: filter_ += f" AND id={id_}" if group else f"id={id_}" if name: filter_ += f' AND name="{name}"' if (group or id_) else f'name="{name}"' response = client.get_remote_network_cidr(range_, filter_, fields) outputs = [{"id": res.get("id"), "name": res.get("name"), "description": res.get("description")} for res in response] headers = ["id", "name", "group", "cidrs", "description"] success_message = "List of the staged remote networks" if response: readable_output = tableToMarkdown(success_message, response, headers=headers) readable_output += ( f"Above results are with page number: {page} and with size: {page_size}." if page and page_size else f"Above results are with limit: {limit if limit else DEFAULT_LIMIT_VALUE}." ) else: readable_output = "No results found." return CommandResults( outputs_prefix="QRadar.RemoteNetworkCIDR", outputs_key_field="id", outputs=outputs, raw_response=response, readable_output=readable_output, ) def qradar_remote_network_cidr_delete_command(client: Client, args) -> CommandResults: """ Args: client (Client): The QRadar client to use. args (dict): Demisto arguments. Returns: Two CommandResults objects, one for the success and one for the failure. """ ids = argToList(args.get("id")) success_delete_ids = [] unsuccessful_delete_ids = [] for id_ in ids: try: client.delete_remote_network_cidr(id_) success_delete_ids.append(id_) except DemistoException as e: unsuccessful_delete_ids.append(assign_params(ID=id_, Error=e.message)) success_human_readable = tableToMarkdown( "Successfully deleted the following remote network(s)", success_delete_ids, headers=["ID"] ) unsuccessful_human_readable = tableToMarkdown( "Failed to delete the following remote network(s)", unsuccessful_delete_ids, headers=["ID", "Error"] ) return CommandResults( readable_output=( (success_human_readable if success_delete_ids else "") + (unsuccessful_human_readable if unsuccessful_delete_ids else "") ) ) def qradar_remote_network_cidr_update_command(client: Client, args): """ Args: client (Client): The QRadar client to use. args (dict): Demisto arguments. Raises: DemistoException: If the args are not valid. Returns: CommandResults. """ id_ = arg_to_number(args.get("id")) name = args.get("name") cidrs_list = argToList(args.get("cidrs")) cidrs_from_query = get_cidrs_indicators(args.get("query")) description = args.get("description") group = args.get("group") fields = args.get("fields") error_message = verify_args_for_remote_network_cidr(cidrs_list, cidrs_from_query, name, group, fields) if error_message: raise DemistoException(error_message) body = {"name": name, "description": description, "cidrs": cidrs_list or cidrs_from_query, "id": id_, "group": group} response = client.create_and_update_remote_network_cidr(body, fields, update=True) success_message = "The staged remote network was successfully updated" outputs = { "id": response.get("id"), "name": response.get("name"), "group": response.get("group"), "description": response.get("description"), } return CommandResults( outputs_prefix="QRadar.RemoteNetworkCIDR", outputs_key_field="id", outputs=outputs, readable_output=tableToMarkdown(success_message, response), raw_response=response, ) def qradar_remote_network_deploy_execution_command(client: Client, args): """ Args: client (Client): The QRadar client to use. args (dict): Demisto arguments. Returns: CommandResults. """ host_ip = args.get("host_ip") status = args.get("status", "INITIATING") deployment_type = args.get("deployment_type") if not re.match(ipv4Regex, host_ip) and not re.match(ipv6Regex, host_ip): raise DemistoException("The host_ip argument is not a valid ip address.") if not status == "INITIATING": raise DemistoException("The status argument must be INITIATING.") if deployment_type not in ["INCREMENTAL", "FULL"]: raise DemistoException("The deployment_type argument must be INCREMENTAL or FULL.") body = {"hosts": [{"ip": host_ip, "status": status}], "type": deployment_type} response = client.remote_network_deploy_execution(body) success_message = "The remote network deploy execution was successfully created." return CommandResults( outputs_prefix="QRadar.deploy", outputs={"status": response["status"]}, readable_output=success_message, raw_response=response, ) def qradar_event_collectors_list_command(client: Client, args: dict) -> CommandResults: """ Retrieves a list of event collectors from QRadar service. possible arguments: - range: Range of offenses to return (e.g.: 0-20, 3-5, 3-3). - filter: Query filter to filter results returned by QRadar service. see https://www.ibm.com/support/knowledgecenter/SS42VS_SHR/com.ibm.qradarapi.doc/c_rest_api_filtering.html for more details. - fields: Use this parameter to specify which fields you would like to get back in the response. Fields that are not named are excluded. Specify subfields in brackets and multiple fields in the same object are separated by commas. - id: If used, will fetch only the specified log source. Args: client (Client): QRadar client to perform the API call. args (Dict): Demisto args. Returns: CommandResults. """ range_ = f"items={args.get('range', DEFAULT_RANGE_VALUE)}" filter_ = args.get("filter") fields = args.get("fields") id = args.get("id") # if this call fails, raise an error and stop command execution response = client.get_resource(id, range_, "/config/event_sources/event_collectors", filter_, fields) outputs = sanitize_outputs(response, EVENT_COLLECTOR_RAW_FORMATTED) headers = build_headers(["ID"], set(EVENT_COLLECTOR_RAW_FORMATTED.values())) return CommandResults( readable_output=tableToMarkdown("Event Collectors List", outputs, headers, removeNull=True), outputs_prefix="QRadar.EventCollector", outputs_key_field="ID", outputs=outputs, raw_response=response, ) def qradar_wincollect_destinations_list_command(client: Client, args: dict) -> CommandResults: """ Retrieves a list of WinCollect destinations from QRadar service. possible arguments: - range: Range of offenses to return (e.g.: 0-20, 3-5, 3-3). - filter: Query filter to filter results returned by QRadar service. see https://www.ibm.com/support/knowledgecenter/SS42VS_SHR/com.ibm.qradarapi.doc/c_rest_api_filtering.html for more details. - fields: Use this parameter to specify which fields you would like to get back in the response. Fields that are not named are excluded. Specify subfields in brackets and multiple fields in the same object are separated by commas. - id: If used, will fetch only the specified log source. Args: client (Client): QRadar client to perform the API call. args (Dict): Demisto args. Returns: CommandResults. """ range_ = f"items={args.get('range', DEFAULT_RANGE_VALUE)}" filter_ = args.get("filter") fields = args.get("fields") endpoint = "/config/event_sources/wincollect/wincollect_destinations" id = args.get("id") # if this call fails, raise an error and stop command execution response = client.get_resource(id, range_, endpoint, filter_, fields) outputs = sanitize_outputs(response, WINCOLLECT_DESTINATION_RAW_FORMATTED) headers = build_headers(["ID"], set(WINCOLLECT_DESTINATION_RAW_FORMATTED.values())) return CommandResults( readable_output=tableToMarkdown("WinCollect Destinations List", outputs, headers, removeNull=True), outputs_prefix="QRadar.WinCollectDestination", outputs_key_field="ID", outputs=outputs, raw_response=response, ) def qradar_disconnected_log_collectors_list_command(client: Client, args: dict) -> CommandResults: """ Retrieves a list of disconnected log collectors from QRadar service. possible arguments: - range: Range of offenses to return (e.g.: 0-20, 3-5, 3-3). - filter: Query filter to filter results returned by QRadar service. see https://www.ibm.com/support/knowledgecenter/SS42VS_SHR/com.ibm.qradarapi.doc/c_rest_api_filtering.html for more details. - fields: Use this parameter to specify which fields you would like to get back in the response. Fields that are not named are excluded. Specify subfields in brackets and multiple fields in the same object are separated by commas. - id: If used, will fetch only the specified log source. Args: client (Client): QRadar client to perform the API call. args (Dict): Demisto args. Returns: CommandResults. """ range_ = f"items={args.get('range', DEFAULT_RANGE_VALUE)}" filter_ = args.get("filter") fields = args.get("fields") endpoint = "/config/event_sources/disconnected_log_collectors" id = args.get("id") # if this call fails, raise an error and stop command execution response = client.get_resource(id, range_, endpoint, filter_, fields) outputs = sanitize_outputs(response, DISCONNECTED_LOG_COLLECTOR_RAW_FORMATTED) headers = build_headers(["ID"], set(DISCONNECTED_LOG_COLLECTOR_RAW_FORMATTED.values())) return CommandResults( readable_output=tableToMarkdown("Disconnected Log Collectors List", outputs, headers, removeNull=True), outputs_prefix="QRadar.DisconnectedLogCollector", outputs_key_field="ID", outputs=outputs, raw_response=response, ) def qradar_log_source_types_list_command(client: Client, args: dict) -> CommandResults: """ Retrieves a list of log source types from QRadar service. possible arguments: - range: Range of offenses to return (e.g.: 0-20, 3-5, 3-3). - filter: Query filter to filter results returned by QRadar service. see https://www.ibm.com/support/knowledgecenter/SS42VS_SHR/com.ibm.qradarapi.doc/c_rest_api_filtering.html for more details. - fields: Use this parameter to specify which fields you would like to get back in the response. Fields that are not named are excluded. Specify subfields in brackets and multiple fields in the same object are separated by commas. - id: If used, will fetch only the specified log source. Args: client (Client): QRadar client to perform the API call. args (Dict): Demisto args. Returns: CommandResults. """ range_ = f"items={args.get('range', DEFAULT_RANGE_VALUE)}" filter_ = args.get("filter") fields = args.get("fields") endpoint = "/config/event_sources/log_source_management/log_source_types" id = args.get("id") # if this call fails, raise an error and stop command execution response = client.get_resource(id, range_, endpoint, filter_, fields) outputs = sanitize_outputs(response, LOG_SOURCE_TYPES_RAW_FORMATTED) headers = build_headers(["ID", "Name", "Custom", "Version", "UUID", "SupportedLanguageIDs"], set()) return CommandResults( readable_output=tableToMarkdown("Log Source Types List", outputs, headers, removeNull=True), outputs_prefix="QRadar.LogSourceTypesList", outputs_key_field="ID", outputs=outputs, raw_response=response, ) def qradar_log_source_protocol_types_list_command(client: Client, args: dict) -> CommandResults: """ Retrieves a list of log source types from QRadar service. possible arguments: - range: Range of offenses to return (e.g.: 0-20, 3-5, 3-3). - filter: Query filter to filter results returned by QRadar service. see https://www.ibm.com/support/knowledgecenter/SS42VS_SHR/com.ibm.qradarapi.doc/c_rest_api_filtering.html for more details. - fields: Use this parameter to specify which fields you would like to get back in the response. Fields that are not named are excluded. Specify subfields in brackets and multiple fields in the same object are separated by commas. - id: If used, will fetch only the specified log source. Args: client (Client): QRadar client to perform the API call. args (Dict): Demisto args. Returns: CommandResults. """ range_ = f"items={args.get('range', DEFAULT_RANGE_VALUE)}" filter_ = args.get("filter") fields = args.get("fields") endpoint = "/config/event_sources/log_source_management/protocol_types" id = args.get("id") # if this call fails, raise an error and stop command execution response = client.get_resource(id, range_, endpoint, filter_, fields) outputs = sanitize_outputs(response, LOG_SOURCE_PROTOCOL_TYPE_RAW_FORMATTED) headers = ["ID", "Name", "CanCollectEvents", "Testable", "CanAcceptSampleEvents"] readable_outputs = [ { **protocol_type, "CanCollectEvents": protocol_type["TestingCapabilities"]["can_collect_events"], "Testable": protocol_type["TestingCapabilities"]["testable"], "CanAcceptSampleEvents": protocol_type["TestingCapabilities"]["can_accept_sample_events"], } for protocol_type in outputs ] return CommandResults( readable_output=tableToMarkdown("Log Source Protocol Types", readable_outputs, headers, removeNull=True), outputs_prefix="QRadar.LogSourceProtocolType", outputs_key_field="ID", outputs=outputs, raw_response=response, ) def qradar_log_source_extensions_list_command(client: Client, args: dict) -> CommandResults: """ Retrieves a list of log source types from QRadar service. possible arguments: - range: Range of offenses to return (e.g.: 0-20, 3-5, 3-3). - filter: Query filter to filter results returned by QRadar service. see https://www.ibm.com/support/knowledgecenter/SS42VS_SHR/com.ibm.qradarapi.doc/c_rest_api_filtering.html for more details. - fields: Use this parameter to specify which fields you would like to get back in the response. Fields that are not named are excluded. Specify subfields in brackets and multiple fields in the same object are separated by commas. - id: If used, will fetch only the specified log source. Args: client (Client): QRadar client to perform the API call. args (Dict): Demisto args. Returns: CommandResults. """ range_ = f"items={args.get('range', DEFAULT_RANGE_VALUE)}" filter_ = args.get("filter") fields = args.get("fields") endpoint = "/config/event_sources/log_source_management/log_source_extensions" id = args.get("id") # if this call fails, raise an error and stop command execution response = client.get_resource(id, range_, endpoint, filter_, fields) outputs = sanitize_outputs(response, LOG_SOURCE_EXTENSION_RAW_FORMATTED) headers = build_headers(["ID", "Name", "Description", "UUID"], set()) return CommandResults( readable_output=tableToMarkdown("Log Source Extensions List", outputs, headers, removeNull=True), outputs_prefix="QRadar.LogSourceExtension", outputs_key_field="ID", outputs=outputs, raw_response=response, ) def qradar_log_source_languages_list_command(client: Client, args: dict) -> CommandResults: """ Retrieves a list of log source types from QRadar service. possible arguments: - range: Range of offenses to return (e.g.: 0-20, 3-5, 3-3). - filter: Query filter to filter results returned by QRadar service. see https://www.ibm.com/support/knowledgecenter/SS42VS_SHR/com.ibm.qradarapi.doc/c_rest_api_filtering.html for more details. - fields: Use this parameter to specify which fields you would like to get back in the response. Fields that are not named are excluded. Specify subfields in brackets and multiple fields in the same object are separated by commas. - id: If used, will fetch only the specified log source. Args: client (Client): QRadar client to perform the API call. args (Dict): Demisto args. Returns: CommandResults. """ range_ = f"items={args.get('range', DEFAULT_RANGE_VALUE)}" filter_ = args.get("filter") fields = args.get("fields") endpoint = "/config/event_sources/log_source_management/log_source_languages" id = args.get("id") # if this call fails, raise an error and stop command execution response = client.get_resource(id, range_, endpoint, filter_, fields) outputs = sanitize_outputs(response, LOG_SOURCE_LANGUAGE_RAW_FORMATTED) headers = build_headers(["ID", "Name"], set()) return CommandResults( readable_output=tableToMarkdown("Log Source Languages List", outputs, headers, removeNull=True), outputs_prefix="QRadar.LogSourceLanguage", outputs_key_field="ID", outputs=outputs, raw_response=response, ) def qradar_log_source_groups_list_command(client: Client, args: dict) -> CommandResults: """ Retrieves a list of log source types from QRadar service. possible arguments: - range: Range of offenses to return (e.g.: 0-20, 3-5, 3-3). - filter: Query filter to filter results returned by QRadar service. see https://www.ibm.com/support/knowledgecenter/SS42VS_SHR/com.ibm.qradarapi.doc/c_rest_api_filtering.html for more details. - fields: Use this parameter to specify which fields you would like to get back in the response. Fields that are not named are excluded. Specify subfields in brackets and multiple fields in the same object are separated by commas. - id: If used, will fetch only the specified log source. Args: client (Client): QRadar client to perform the API call. args (Dict): Demisto args. Returns: CommandResults. """ range_ = f"items={args.get('range', DEFAULT_RANGE_VALUE)}" filter_ = args.get("filter") fields = args.get("fields") endpoint = "/config/event_sources/log_source_management/log_source_groups" id = args.get("id") # if this call fails, raise an error and stop command execution response = client.get_resource(id, range_, endpoint, filter_, fields) outputs = sanitize_outputs(response, LOG_SOURCE_GROUP_RAW_FORMATTED) headers = build_headers(["ID", "Name"], set(LOG_SOURCE_GROUP_RAW_FORMATTED.values())) return CommandResults( readable_output=tableToMarkdown("Log Source Groups List", outputs, headers, removeNull=True), outputs_prefix="QRadar.LogSourceGroup", outputs_key_field="ID", outputs=outputs, raw_response=response, ) def qradar_log_source_delete_command(client: Client, args: dict) -> CommandResults: """ Deletes a log source by id or by name. Possible arguments: - name: The unique name of the log source to be deleted. If you don't provide this argument, id is required. - id: The ID of the log source to be deleted. If you don't provide this argument, name is required. Args: client (Client): QRadar client to perform the API call. args (Dict): Demisto args. Returns: CommandResults. """ name = args.get("name") id = args.get("id") if id is not None: try: client.delete_log_source(id) return CommandResults(readable_output=f"Log source {id} was deleted successfully") except DemistoException as e: if e.res.status_code == 404: return CommandResults(readable_output=f"Log source with id {id} does not exist") if name is not None: log_source_list = client.get_resource_list( "items=0-0", "config/event_sources/log_source_management/log_sources", f'name="{name}"' ) if not log_source_list: return CommandResults(readable_output=f"Log source with name {name} does not exist") relevant_log_source = log_source_list[0] client.delete_log_source(relevant_log_source.get("id")) return CommandResults(readable_output=f"Log source {name} was deleted successfully") raise Exception("At least one of the arguments: name, id must be provided.") def qradar_log_source_create_command(client: Client, args: dict) -> CommandResults: """ Creates a log source. Possible arguments: - name: Required. The unique name of the log source. - sending_ip: The ip of the system which the log source is associated to, or fed by. - protocol_type_id: Required. The type of protocol that is used by the log source. - type_id: Required. The type of the log source. Must correspond to an existing log source type. Must correspond to an existing protocol type. - protocol_parameters: Required. The list of protocol parameters corresponding with the selected protocol type id. The syntax for this argument should follow: protocol_parameters="name_1=value_1,name_2=value_2,...,name_n=value_n" where each name should correspond to a name of a protocol parameter from the protocol type and each value should fit the type of the protocol parameter. - descrption: The description of the log source - coalesce_events: Determines if events collected by this log source are coalesced based on common properties. If each individual event is stored, then the condition is set to false. Defaults to true. - enabled: Determines if the log source is enabled. Defaults to true. - parsing_order: The order in which log sources will parse if multiple exists with a common identifier. - group_ids: Required. The set of log source group IDs this log source is a member of. Each ID must correspond to an existing log source group. - credibility: On a scale of 0-10, the amount of credibility that the QRadar administrator places on this log source - store_event_payload: If the payloads of events that are collected by this log source are stored, the condition is set to 'true'. If only the normalized event records are stored, then the condition is set to 'false'. - target_event_collector_id: Required. The ID of the event collector where the log source sends its data. The ID must correspond to an existing event collector. - disconnected_log_collector_id: The ID of the disconnected log collector where this log source will run. The ID must correspond to an existing disconnected log collector. - language_id: The language of the events that are being processed by this log source. Must correspond to an existing log source language. - requires_deploy: Set to 'true' if you need to deploy changes to enable the log source for use; otherwise, set to 'false' if the log source is already active. - wincollect_internal_destination_id : The internal WinCollect destination for this log source, if applicable. Log sources without an associated WinCollect agent have a null value. Must correspond to an existing WinCollect destination. - wincollect_external_destination_ids: The set of external WinCollect destinations for this log source, if applicable. Log Sources without an associated WinCollect agent have a null value. Each ID must correspond to an existing WinCollect destination. - gateway: If the log source is configured as a gateway, the condition is set to 'true'; otherwise, the condition is set to 'false'. A gateway log source is a stand-alone protocol configuration. The log source receives no events itself, and serves as a host for a protocol configuration that retrieves event data to feed other log sources. It acts as a "gateway" for events from multiple systems to enter the event pipeline. """ log_source = parse_log_source(args) response = client.create_log_source(log_source) outputs = sanitize_outputs(response, LOG_SOURCES_RAW_FORMATTED)[0] headers = build_headers(["ID", "Name", "Description"], set(LOG_SOURCES_RAW_FORMATTED.values())) readable_outputs = { "ID": outputs["ID"], "Name": outputs["Name"], "CreationDate": outputs["CreationDate"], "Description": outputs["Description"], "Enabled": outputs["Enabled"], "Status": outputs["Status"]["status"], "StatusLastUpdated": outputs["Status"].get("last_updated", ""), "StatusMessages": outputs["Status"].get("messages", ""), } return CommandResults( readable_output=tableToMarkdown("Log Source Created", readable_outputs, headers, removeNull=True), outputs_prefix="QRadar.LogSource", outputs_key_field="ID", outputs=outputs, raw_response=response, ) def qradar_log_source_update_command(client: Client, args: dict) -> CommandResults: """ Creates a log source. Possible arguments: - id: Required. The id of the log source. - name: The unique name of the log source. - sending_ip: The ip of the system which the log source is associated to, or fed by. - protocol_type_id: The type of protocol that is used by the log source. - type_id: The type of the log source. Must correspond to an existing log source type. Must correspond to an existing protocol type. - protocol_parameters: The list of protocol parameters corresponding with the selected protocol type id. The syntax for this argument should follow: protocol_parameters="name_1=value_1,name_2=value_2,...,name_n=value_n" where each name should correspond to a name of a protocol parameter from the protocol type and each value should fit the type of the protocol parameter. - descrption: The description of the log source - coalesce_events: Determines if events collected by this log source are coalesced based on common properties. If each individual event is stored, then the condition is set to false. Defaults to true. - enabled: Determines if the log source is enabled. Defaults to true. - parsing_order: The order in which log sources will parse if multiple exists with a common identifier. - group_ids: The set of log source group IDs this log source is a member of. Each ID must correspond to an existing log source group. - credibility: On a scale of 0-10, the amount of credibility that the QRadar administrator places on this log source - store_event_payload: If the payloads of events that are collected by this log source are stored, the condition is set to 'true'. If only the normalized event records are stored, then the condition is set to 'false'. - target_event_collector_id: The ID of the event collector where the log source sends its data. The ID must correspond to an existing event collector. - disconnected_log_collector_id: The ID of the disconnected log collector where this log source will run. The ID must correspond to an existing disconnected log collector. - language_id: The language of the events that are being processed by this log source. Must correspond to an existing log source language. - requires_deploy: Set to 'true' if you need to deploy changes to enable the log source for use; otherwise, set to 'false' if the log source is already active. - wincollect_internal_destination_id : The internal WinCollect destination for this log source, if applicable. Log sources without an associated WinCollect agent have a null value. Must correspond to an existing WinCollect destination. - wincollect_external_destination_ids: The set of external WinCollect destinations for this log source, if applicable. Log Sources without an associated WinCollect agent have a null value. Each ID must correspond to an existing WinCollect destination. - gateway: If the log source is configured as a gateway, the condition is set to 'true'; otherwise, the condition is set to 'false'. A gateway log source is a stand-alone protocol configuration. The log source receives no events itself, and serves as a host for a protocol configuration that retrieves event data to feed other log sources. It acts as a "gateway" for events from multiple systems to enter the event pipeline. """ id = args.get("id") log_source = parse_partial_log_source(args) client.update_log_source(log_source) return CommandResults(readable_output=f"Log source {id} was updated successfully") def migrate_integration_ctx(ctx: dict) -> dict: """Migrates the old context to the current context Args: ctx: The context_data to simplify Returns: The cleared context_data """ fetch_id_ctx: str = ctx.get(LAST_FETCH_KEY, "0") try: fetch_id = int(fetch_id_ctx) except ValueError: try: fetch_id = int(json.loads(fetch_id_ctx)) except ValueError: print_debug_msg(f"Could not retrieve LAST_FETCH_KEY from {fetch_id_ctx} Setting to 0") fetch_id = 0 last_update_ctx: str = ctx.get(LAST_MIRROR_KEY, "0") try: last_update = int(last_update_ctx) except ValueError: try: last_update = int(json.loads(last_update_ctx)) except ValueError: print_debug_msg(f"Could not retrieve last_mirror_update from {last_update_ctx} Setting to 0") last_update = 0 mirrored_offenses: dict[str, str] = {} try: for key in ("mirrored_offenses", "updated_mirrored_offenses", "resubmitted_mirrored_offenses"): mirrored_offenses |= { json.loads(offense).get("id"): QueryStatus.WAIT.value for offense in json.loads(ctx.get(key, "[]")) } except Exception as e: print_debug_msg(f"Could not load mirrored_offenses from context_data. Error: {e}") return { LAST_FETCH_KEY: fetch_id, LAST_MIRROR_KEY: last_update, MIRRORED_OFFENSES_QUERIED_CTX_KEY: mirrored_offenses, MIRRORED_OFFENSES_FINISHED_CTX_KEY: {}, MIRRORED_OFFENSES_FETCHED_CTX_KEY: {}, SAMPLE_INCIDENTS_KEY: [], } def qradar_print_context_command() -> CommandResults: """Returns a redacted snapshot of integration context metrics for debugging. Returns: CommandResults: Command results with human-readable output and context output. """ ctx = qradar_get_integration_context() queried = ctx.get(MIRRORED_OFFENSES_QUERIED_CTX_KEY, {}) or {} finished = ctx.get(MIRRORED_OFFENSES_FINISHED_CTX_KEY, {}) or {} fetched = ctx.get(MIRRORED_OFFENSES_FETCHED_CTX_KEY, {}) or {} in_progress = {k: v for k, v in queried.items() if v not in QueryStatus.values()} waiting = [k for k, v in queried.items() if v in QueryStatus.values()] concurrent_searches_count = get_current_concurrent_searches(ctx) summary = { "queried_count": len(queried), "finished_count": len(finished), "fetched_count": len(fetched), "in_progress_count": len(in_progress), "waiting_markers_count": len(waiting), "concurrent_active": concurrent_searches_count, "samples_count": len(ctx.get(SAMPLE_INCIDENTS_KEY, [])), } readable = tableToMarkdown("Summary", [summary], removeNull=True) return CommandResults(readable_output=readable, outputs_prefix="QRadar.Debug", outputs={"snapshot": summary}) def validate_integration_context() -> None: """ The new context structure consists of two dictionaries of queried offenses and finished offenses. Some older instances might not have them, so we fix that. """ context_data = qradar_get_integration_context() new_ctx = context_data.copy() try: print_context_data_stats(context_data, "Checking ctx") print_debug_msg("Context is with the new mirroring standard") extract_works = True except Exception as e: print_debug_msg(f"Checking {context_data} failed, trying to make it retry compatible. Error was: {e!s}") extract_works = False if not extract_works: # Scenario: The old context structure is invalid/unreadable. cleared_ctx = migrate_integration_ctx(new_ctx) print_debug_msg(f"Change ctx context data was cleared and changing to {cleared_ctx}") # Merge the entire new dict. This effectively replaces the old context. safely_update_context_data_partial(cleared_ctx, override_keys=list(cleared_ctx.keys())) print_debug_msg(f"Change ctx context data was cleared and changed to {cleared_ctx}") elif MIRRORED_OFFENSES_FETCHED_CTX_KEY not in context_data: # Scenario: context is fine, but missing the 'mirrored_offenses_fetched' sub-dict. print_debug_msg(f"Adding {MIRRORED_OFFENSES_FETCHED_CTX_KEY} to context") partial_changes: dict = {MIRRORED_OFFENSES_FETCHED_CTX_KEY: {}} safely_update_context_data_partial(partial_changes) """ MAIN FUNCTION """ def main() -> None: # pragma: no cover params = demisto.params() command = demisto.command() args = demisto.args() # handle allowed advanced parameters adv_params = params.get("adv_params") if adv_params: try: globals_ = globals() for adv_p in adv_params.split(","): adv_p_kv = [item.strip() for item in adv_p.split("=")] if len(adv_p_kv) != 2: raise DemistoException( f"Failed to parse advanced parameter: {adv_p} - please make sure you entered it correctly." ) adv_param_name = adv_p_kv[0] if adv_param_name in ADVANCED_PARAMETERS_STRING_NAMES: globals_[adv_p_kv[0]] = adv_p_kv[1] elif adv_param_name in ADVANCED_PARAMETER_INT_NAMES: globals_[adv_p_kv[0]] = int(adv_p_kv[1]) else: raise DemistoException(f"The parameter: {adv_p_kv[0]} is not a valid advanced parameter. Please remove it") except DemistoException as e: raise DemistoException(f"Failed to parse advanced params. Error: {e.message}") from e except Exception as e: raise DemistoException(f"Failed to parse advanced params. Error: {e}") from e server = params.get("server") verify_certificate = not params.get("insecure", False) proxy = params.get("proxy", False) api_version = params.get("api_version") if api_version and float(api_version) < MINIMUM_API_VERSION: raise DemistoException(f"API version cannot be lower than {MINIMUM_API_VERSION}") credentials = params.get("credentials") timeout = arg_to_number(params.get("timeout")) try: client = Client( server=server, verify=verify_certificate, proxy=proxy, api_version=api_version, credentials=credentials, timeout=timeout, ) # All command names with or are for supporting QRadar v2 command names for backward compatibility if command == "test-module": validate_integration_context() return_results(test_module_command(client, params)) elif command == "fetch-incidents": demisto.incidents(fetch_incidents_command()) elif command == "long-running-execution": validate_integration_context() support_multithreading() long_running_execution_command(client, params) elif command in [ "qradar-offenses-list", "qradar-offenses", "qradar-offense-by-id", ]: return_results(qradar_offenses_list_command(client, args)) elif command in ["qradar-offense-update", "qradar-update-offense"]: return_results(qradar_offense_update_command(client, args)) elif command in ["qradar-closing-reasons", "qradar-get-closing-reasons"]: return_results(qradar_closing_reasons_list_command(client, args)) elif command in ["qradar-offense-notes-list", "qradar-get-note"]: return_results(qradar_offense_notes_list_command(client, args)) elif command in ["qradar-offense-note-create", "qradar-create-note"]: return_results(qradar_offense_notes_create_command(client, args)) elif command == "qradar-rules-list": return_results(qradar_rules_list_command(client, args)) elif command == "qradar-rule-groups-list": return_results(qradar_rule_groups_list_command(client, args)) elif command in [ "qradar-assets-list", "qradar-get-assets", "qradar-get-asset-by-id", ]: return_results(qradar_assets_list_command(client, args)) elif command == "qradar-saved-searches-list": return_results(qradar_saved_searches_list_command(client, args)) elif command == "qradar-searches-list": return_results(qradar_searches_list_command(client, args)) elif command in ["qradar-search-create", "qradar-searches"]: return_results(qradar_search_create_command(client, params, args)) elif command in ["qradar-search-status-get", "qradar-get-search"]: return_results(qradar_search_status_get_command(client, args)) elif command in [ "qradar-search-results-get", "qradar-get-search-results", ]: return_results(qradar_search_results_get_command(client, args)) elif command == "qradar-search-cancel": return_results(qradar_search_cancel_command(client, args)) elif command == "qradar-search-delete": return_results(qradar_search_delete_command(client, args)) elif command in [ "qradar-reference-sets-list", "qradar-get-reference-by-name", ]: return_results(qradar_reference_sets_list_command(client, args)) elif command in [ "qradar-reference-set-create", "qradar-create-reference-set", ]: return_results(qradar_reference_set_create_command(client, args)) elif command in [ "qradar-reference-set-delete", "qradar-delete-reference-set", ]: return_results(qradar_reference_set_delete_command(client, args)) elif command in [ "qradar-reference-set-value-upsert", "qradar-create-reference-set-value", "qradar-update-reference-set-value", ]: return_results(qradar_reference_set_value_upsert_command(args, client, params)) elif command in [ "qradar-reference-set-value-delete", "qradar-delete-reference-set-value", ]: return_results(qradar_reference_set_value_delete_command(client, args)) elif command in [ "qradar-domains-list", "qradar-get-domains", "qradar-get-domain-by-id", ]: return_results(qradar_domains_list_command(client, args)) elif command in ["qradar-indicators-upload", "qradar-upload-indicators"]: return_results(qradar_indicators_upload_command(args, client, params)) elif command == "qradar-geolocations-for-ip": return_results(qradar_geolocations_for_ip_command(client, args)) elif command == "qradar-log-sources-list": return_results(qradar_log_sources_list_command(client, args)) elif command == "qradar-get-custom-properties": return_results(qradar_get_custom_properties_command(client, args)) elif command == "qradar-ips-source-get": return_results(qradar_ips_source_get_command(client, args)) elif command == "qradar-ips-local-destination-get": return_results(qradar_ips_local_destination_get_command(client, args)) elif command == "qradar-reset-last-run": return_results(qradar_reset_last_run_command()) elif command == "get-mapping-fields": return_results(qradar_get_mapping_fields_command(client)) elif command == "get-remote-data": validate_integration_context() return_results(get_remote_data_command(client, params, args)) elif command == "get-modified-remote-data": validate_integration_context() return_results(get_modified_remote_data_command(client, params, args)) elif command == "qradar-search-retrieve-events": return_results(qradar_search_retrieve_events_command(client, params, args)) elif command == "qradar-remote-network-cidr-create": return_results(qradar_remote_network_cidr_create_command(client, args)) elif command == "qradar-remote-network-cidr-list": return_results(qradar_remote_network_cidr_list_command(client, args)) elif command == "qradar-remote-network-cidr-delete": return_results(qradar_remote_network_cidr_delete_command(client, args)) elif command == "qradar-remote-network-cidr-update": return_results(qradar_remote_network_cidr_update_command(client, args)) elif command == "qradar-remote-network-deploy-execution": return_results(qradar_remote_network_deploy_execution_command(client, args)) elif command == "qradar-event-collectors-list": return_results(qradar_event_collectors_list_command(client, args)) elif command == "qradar-wincollect-destinations-list": return_results(qradar_wincollect_destinations_list_command(client, args)) elif command == "qradar-disconnected-log-collectors-list": return_results(qradar_disconnected_log_collectors_list_command(client, args)) elif command == "qradar-log-source-types-list": return_results(qradar_log_source_types_list_command(client, args)) elif command == "qradar-log-source-protocol-types-list": return_results(qradar_log_source_protocol_types_list_command(client, args)) elif command == "qradar-log-source-extensions-list": return_results(qradar_log_source_extensions_list_command(client, args)) elif command == "qradar-log-source-languages-list": return_results(qradar_log_source_languages_list_command(client, args)) elif command == "qradar-log-source-groups-list": return_results(qradar_log_source_groups_list_command(client, args)) elif command == "qradar-log-source-delete": return_results(qradar_log_source_delete_command(client, args)) elif command == "qradar-log-source-create": return_results(qradar_log_source_create_command(client, args)) elif command == "qradar-log-source-update": return_results(qradar_log_source_update_command(client, args)) elif command == "qradar-print-context": return_results(qradar_print_context_command()) elif command == "qradar-reference-maps-list": return_results(qradar_reference_maps_list_command(client, args)) elif command == "qradar-reference-map-value-upsert": return_results(qradar_reference_map_value_upsert_command(client, args)) else: raise NotImplementedError(f"""Command '{command}' is not implemented.""") except Exception as e: print_debug_msg(f"The integration context_data is {qradar_get_integration_context()}") return_error(f"Failed to execute {demisto.command()} command.\nError:\n{traceback.format_exc()}\nException is: {e!s}") finally: # CIAC-10628 if command not in ("test-module", "fetch-incidents", "long-running-execution"): client._return_execution_metrics_results() client.execution_metrics.metrics = None """ ENTRY POINT """ if __name__ in ("__main__", "__builtin__", "builtins"): register_signal_handler_profiling_dump(profiling_dump_rows_limit=PROFILING_DUMP_ROWS_LIMIT) main()