IronDefense
The IronDefense Integration for Cortex XSOAR allows users to interact with IronDefense alerts within Cortex XSOAR. The Integration provides the ability to rate alerts, update alert statuses, add comments to alerts, to report observed bad activity, get alerts, get events, and get IronDome information.
Network Security · IronNet
Details
| ID | IronDefense |
|---|---|
| Provider | IronNet Cybersecurity |
| Category | Network Security |
| From Version | 5.0.0 |
| Docker Image | demisto/python3:3.12.13.10116658 |
| Supported Modules | Agentix XSIAM |
README
IronDefense gives users the ability to rate alerts, update alert statuses, add comments to alerts, and to report observed bad activity.
Configure IronDefense on XSOAR
- Navigate to Settings > Integrations > Servers & Services.
- Search for IronDefense.
- Click Add instance to create and configure a new integration instance.
- Name: a textual name for the integration instance.
- IronAPI Host/IP
- IronAPI Port
- Username
- Request Timeout (Sec)
- Click Test to validate the new instance.
Commands
You can execute these commands from the XSOAR CLI, as part of an automation, or in a playbook. After you successfully execute a command, a DBot message appears in the War Room with the command details.
- Rate an alert: irondefense-rate-alert
- Add a comment to an alert: irondefense-comment-alert
- Set the status of an alert: irondefense-set-alert-status
- Submit an observed bad endpoint to create Threat Intelligence Rules (TIR): irondefense-report-observed-bad-activity
1. Rate an alert
Rates an IronDefense alert.
Base Command
irondefense-rate-alert
Input
| Argument Name | Description | Required |
|---|---|---|
| alert_id | The ID of the IronDefense alert. | Required |
| severity | The severity rating of the alert. Can be: "Undecided", "Benign", "Suspicious", "Malicious". | Required |
| expectation | Determines whether the rating was expected. Can be: "Unknown", "Expected", "Unexpected". Use "Unknown" if the rating is undecided. | Required |
| comments | Explains the rating of the alert. | Required |
| share_comment_with_irondome | Whether to share the comment with IronDome. | Required |
Context Output
There are no context output for this command.
2. Add a comment to an alert
Adds a comment to an IronDefense alert.
Base Command
irondefense-comment-alert
Input
| Argument Name | Description | Required |
|---|---|---|
| alert_id | The ID of the IronDefense alert. | Required |
| comment | Explains the rating of the alert. | Required |
| share_comment_with_irondome | Whether to share the comment with IronDome. | Required |
Context Output
There are no context output for this command.
3. Set the status of an alert
Sets the status of an IronDefense alert.
Base Command
irondefense-set-alert-status
Input
| Argument Name | Description | Required |
|---|---|---|
| alert_id | The ID of the IronDefense alert. | Required |
| status | The alert status to set. Can be: "Awaiting Review", "Under Review", "Closed". | Required |
| comments | Explains the status of the alert. | Required |
| share_comment_with_irondome | Whether to share the comment with IronDome. | Required |
Context Output
There are no context output for this command.
4. Submit an observed bad endpoint to create Threat Intelligence Rules (TIR)
Submits an observed bad endpoint to IronDefense to create Threat Intelligence Rules (TIR).
Base Command
irondefense-report-observed-bad-activity
Input
| Argument Name | Description | Required |
|---|---|---|
| name | The name of the Threat Intelligence Rule (TIR) to be created. | Required |
| description | A description of the observed bad endpoint. | Required |
| ip | The IP address of the observed bad endpoint. | Optional |
| domain | The domain name of the observed bad endpoint. | Optional |
| activity_start_time | The start time of the observed bad activity in RFC 3339 format. | Required |
| activity_end_time | The end time of the observed bad activity in RFC 3339 format. | Required |
Context Output
There are no context output for this command.
irondefense-get-event
Retrieves an IronDefense event.
Base Command
irondefense-get-event
Input
| Argument Name | Description | Required |
|---|---|---|
| event_id | The ID of the IronDefense event. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| IronDefense.Event.id | String | The event ID. |
| IronDefense.Event.alert_id | String | The associated alert ID. |
| IronDefense.Event.category | String | The event category. |
| IronDefense.Event.sub_category | String | The event sub category. |
| IronDefense.Event.severity | Number | The event severity (0-1000). |
| IronDefense.Event.confidence | Number | The event confidence (real number between 0-1). |
| IronDefense.Event.created | Date | Time the event was created. |
| IronDefense.Event.updated | Date | Time the event was last updated. |
| IronDefense.Event.start_time | Date | The start time of this event’s activity. |
| IronDefense.Event.end_time | Date | The end time of this event’s activity. |
| IronDefense.Event.iron_dome_shared_time | Date | The time when the event was sent to IronDome - not present if not shared with irondome. |
| IronDefense.Event.is_whitelisted | Boolean | True if the event activity was whitelisted, false otherwise. |
| IronDefense.Event.is_blacklisted | Boolean | True if the event activity was blacklisted, false otherwise. |
| IronDefense.Event.src_ip | String | The source IP associated with this event. |
| IronDefense.Event.dst_ip | String | The destination IP associated with this event. |
| IronDefense.Event.dst_port | Number | The destination port associated with this event. |
| IronDefense.Event.ppp_domains | String | Any domains associated with this event. |
| IronDefense.Event.primary_app_protocol | String | The primary application protocol associated with this event. |
| IronDefense.Event.secondary_app_protocol | String | The secondary application protocol associated with this event. |
| IronDefense.Event.bytes_in | Number | The byte count of incoming traffic for this event. |
| IronDefense.Event.bytes_out | Number | The byte count of outgoing traffic for this event. |
| IronDefense.Event.total_bytes | Number | The byte count of the total traffic (in either direction) for this event. |
| IronDefense.Event.url | String | The related URL for this event, if applicable. |
| IronDefense.Event.raw_data_formats | String | The list of distinct raw data formats for this event. |
| IronDefense.Event.src_entity_attribute | String | The source entity attribute related to this event, if one could be determined. |
| IronDefense.Event.src_entity_attribute_type | String | The source entity attribute type related to this event, if one could be determined. |
| IronDefense.Event.dst_entity_attribute | String | The destination entity attribute related to this event, if one could be determined. |
| IronDefense.Event.dst_entity_attribute_type | String | The destination entity attribute type related to this event, if one could be determined. |
| IronDefense.Event.vue_url | String | The url for displaying the event within IronVUE. |
| IronDefense.Event.Context.name | String | The name of the context table. |
| IronDefense.Event.Context.columns | String | Column values for the context table. |
irondefense-get-alerts
Pulls Alerts from IronDefense.
Base Command
irondefense-get-alerts
Input
| Argument Name | Description | Required |
|---|---|---|
| alert_id | The list of Alert IDs to filter by. | Optional |
| category | The list of Alert categories to filter by. Possible values are: C2, Action, Access, Recon, Other. | Optional |
| sub_category | The list of Alert sub categories to filter by. | Optional |
| status | The list of Alert status to filter by. Possible values are: Awaiting Review, Under Review, Closed. | Optional |
| analyst_severity | The list of Alert analyst severity to filter by. Possible values are: Undecided, Benign, Suspicious, Malicious. | Optional |
| analyst_expectation | The list of Alert analyst expectation to filter by. Possible values are: Unknown, Expected, Unexpected. | Optional |
| min_severity | The minimum Alert severity to filter by. (0-1000). | Optional |
| max_severity | The maximum Alert severity to filter by. (0-1000). | Optional |
| min_created | The minimum Alert created date to filter by in RFC 3339 format (E.g. 2017-10-13T07:20:50.52Z). | Optional |
| max_created | The maximum Alert created date to filter by in RFC 3339 format (E.g. 2017-10-13T07:20:50.52Z). | Optional |
| min_updated | The minimum Alert updated date to filter by in RFC 3339 format (E.g. 2017-10-13T07:20:50.52Z). | Optional |
| max_updated | The maximum Alert updated date to filter by in RFC 3339 format (E.g. 2017-10-13T07:20:50.52Z). | Optional |
| min_first_event_created | The minimum Alert first event created date to filter by in RFC 3339 format (E.g. 2017-10-13T07:20:50.52Z). | Optional |
| max_first_event_created | The maximum Alert first event created date to filter by in RFC 3339 format (E.g. 2017-10-13T07:20:50.52Z). | Optional |
| min_last_event_created | The minimum Alert last event created date to filter by in RFC 3339 format (E.g. 2017-10-13T07:20:50.52Z). | Optional |
| max_last_event_created | The maximum Alert last event created date to filter by in RFC 3339 format (E.g. 2017-10-13T07:20:50.52Z). | Optional |
| min_first_event_start_time | The minimum Alert first event start date to filter by in RFC 3339 format (E.g. 2017-10-13T07:20:50.52Z). | Optional |
| max_first_event_start_time | The maximum Alert first event start date to filter by in RFC 3339 format (E.g. 2017-10-13T07:20:50.52Z). | Optional |
| min_last_event_end_time | The minimum Alert last event end date to filter by in RFC 3339 format (E.g. 2017-10-13T07:20:50.52Z). | Optional |
| max_last_event_end_time | The maximum Alert last event end date to filter by in RFC 3339 format (E.g. 2017-10-13T07:20:50.52Z). | Optional |
| analytic_version | The list of Alert analytic versions to filter by. | Optional |
| limit | The limit on the number of Alerts to be returned. | Optional |
| offset | The number of results to skip - used for paging the results. | Optional |
| sort | The list of Alert fields to sort by. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| IronDefense.Alert.id | string | The ID of the alert. |
| IronDefense.Alert.category | string | The category of the alert. |
| IronDefense.Alert.sub_category | string | The subcategory of the alert. |
| IronDefense.Alert.severity | Number | The severity score of the alert. (0-1000) |
| IronDefense.Alert.status | String | The status of the alert. |
| IronDefense.Alert.analyst_severity | String | The analyst severity of the alert. |
| IronDefense.Alert.analyst_expectation | String | The analyst expectation of the alert. |
| IronDefense.Alert.created | Date | Time the alert was created in IronDefense. |
| IronDefense.Alert.updated | Date | Time the alert was last updated in IronDefense. |
| IronDefense.Alert.event_count | Number | The number of non-whitelisted events associated with this alert. |
| IronDefense.Alert.FirstEventCreated | String | The earliest created date of any associated event on this alert. |
| IronDefense.Alert.last_event_created | String | The last created date of any associated event on this alert. |
| IronDefense.Alert.raw_data_format | String | List of distinct raw data formats for this event. |
| IronDefense.Alert.aggregation_criteria | String | Criteria used to build alert, specific to the event context fields. |
| IronDefense.Alert.vue_url | String | The url for displaying the alert within IronVUE. |
| IronDefense.Query.GetAlerts.limit | Number | The maximum number of results that were requested to be returned. |
| IronDefense.Query.GetAlerts.offset | Number | The number of results that were skipped - used for paging the results. |
| IronDefense.Query.GetAlerts.total | Number | The total number of results possible from the query. |
irondefense-get-alert-irondome-information
Retrieves IronDome information for an IronDefense alert.
Base Command
irondefense-get-alert-irondome-information
Input
| Argument Name | Description | Required |
|---|---|---|
| alert_id | The ID of the IronDefense alert. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| IronDome.Correlations.alert_id | String | The alert ID the correlations are associated with. |
| IronDome.Correlations.dome_tag | String | The IronDome tag. |
| IronDome.Correlations.correlation.correlations.ip | String | The IP correlated on (if an IP correlation). |
| IronDome.Correlations.correlation.correlations.domain | String | The Domain correlated on (if a Domain correlation). |
| IronDome.Correlations.correlation.correlations.behavior | Boolean | True if the correlation was behavior-based. |
| IronDome.Correlations.correlation.correlations.enterprise_correlations | Number | The number of enterprise correlations. |
| IronDome.Correlations.correlation.correlations.community_correlations | Number | The number of community correlations. |
| IronDome.CorrelationParticipation.alert_id | String | The alert ID the correlation participants are associated with. |
| IronDome.CorrelationParticipation.correlation_participation.dome_tag | String | The IronDome tag. |
| IronDome.CorrelationParticipation.correlation_participation.behavior.malicious_count | Number | Count of malicious ratings. |
| IronDome.CorrelationParticipation.correlation_participation.behavior.suspicious_count | Number | Count of suspicious ratings. |
| IronDome.CorrelationParticipation.correlation_participation.behavior.benign_count | Number | Count of benign ratings. |
| IronDome.CorrelationParticipation.correlation_participation.behavior.whitelisted_count | Number | Count of whitelisted ratings. |
| IronDome.CorrelationParticipation.correlation_participation.behavior.comments_count | Number | Count of comments made. |
| IronDome.CorrelationParticipation.correlation_participation.behavior.activity_count | Number | Count of activity. |
| IronDome.CorrelationParticipation.correlation_participation.behavior.resource_owner | Boolean | True if the caller is the resource owner. |
| IronDome.CorrelationParticipation.correlation_participation.behavior.first_seen | Date | The time the activity was first seen. |
| IronDome.CorrelationParticipation.correlation_participation.behavior.last_seen | Date | The time the activity was last seen. |
| IronDome.CorrelationParticipation.correlation_participation.domain.malicious_count | Number | Count of malicious ratings. |
| IronDome.CorrelationParticipation.correlation_participation.domain.suspicious_count | Number | Count of suspicious ratings. |
| IronDome.CorrelationParticipation.correlation_participation.domain.benign_count | Number | Count of benign ratings. |
| IronDome.CorrelationParticipation.correlation_participation.domain.whitelisted_count | Number | Count of whitelisted ratings. |
| IronDome.CorrelationParticipation.correlation_participation.domain.comments_count | Number | Count of comments made. |
| IronDome.CorrelationParticipation.correlation_participation.domain.activity_count | Number | Count of activity. |
| IronDome.CorrelationParticipation.correlation_participation.domain.resource_owner | Boolean | True if the caller is the resource owner. |
| IronDome.CorrelationParticipation.correlation_participation.domain.first_seen | Date | The time the activity was first seen. |
| IronDome.CorrelationParticipation.correlation_participation.domain.last_seen | Date | The time the activity was last seen. |
| IronDome.CorrelationParticipation.correlation_participation.ip.malicious_count | Number | Count of malicious ratings. |
| IronDome.CorrelationParticipation.correlation_participation.ip.suspicious_count | Number | Count of suspicious ratings. |
| IronDome.CorrelationParticipation.correlation_participation.ip.benign_count | Number | Count of benign ratings. |
| IronDome.CorrelationParticipation.correlation_participation.ip.whitelisted_count | Number | Count of whitelisted ratings. |
| IronDome.CorrelationParticipation.correlation_participation.ip.comments_count | Number | Count of comments made. |
| IronDome.CorrelationParticipation.correlation_participation.ip.activity_count | Number | Count of activity. |
| IronDome.CorrelationParticipation.correlation_participation.ip.resource_owner | Boolean | True if the caller is the resource owner. |
| IronDome.CorrelationParticipation.correlation_participation.ip.first_seen | Date | The time the activity was first seen. |
| IronDome.CorrelationParticipation.correlation_participation.ip.last_seen | Date | The time the activity was last seen. |
| IronDome.CommunityComments.alert_id | String | The alert ID associated with the community comments. |
| IronDome.CommunityComments.community_comments.created | Date | The time that the comment was created. |
| IronDome.CommunityComments.community_comments.comment | String | The comment text. |
| IronDome.CommunityComments.community_comments.dome_tags | String | The IronDome tags related to the comment. |
| IronDome.CommunityComments.community_comments.enterprise | Boolean | True if enterprise. |
| IronDome.CommunityComments.community_comments.self | Boolean | True if the comment was made by the caller. |
| IronDome.CognitiveSystemScore.alert_id | String | The alert ID associated with the cognitive system score. |
| IronDome.CognitiveSystemScore.cognitive_system_score | Number | The cognitive system score of the alert. |
| IronDome.Notification.alert_id | String | The alert ID associated with the IronDome notification. |
| IronDome.Notification.dome_notification.id | String | The unique ID of the notification. |
| IronDome.Notification.dome_notification.category | String | The category of Dome notification. |
| IronDome.Notification.dome_notification.created | Date | The category of Dome notification. |
| IronDome.Notification.dome_notification.dome_tags | String | The IronDome tags related to this notification. |
| IronDome.Notification.dome_notification.alert_ids | String | The IDs of the alerts related to this notification. |
| IronDome.Notification.dome_notification.mismatch_details.enterprise_severities.analyst_severity | String | The mismatched severity across correlated alerts within the enterprise. |
| IronDome.Notification.dome_notification.mismatch_details.enterprise_severities.count | Number | The count of mismatched severity with this analyst rating. |
| IronDome.Notification.dome_notification.mismatch_details.community_severities.analyst_severity | String | The mismatched severity across correlated alerts within the community. |
| IronDome.Notification.dome_notification.mismatch_details.community_severities.count | Number | The count of mismatched severity with this analyst rating. |
| IronDome.Notification.dome_notification.severity_details.analyst_severity | String | The analyst severity of the alert. |
| IronDome.Notification.dome_notification.comment_details.comment | String | The comment text in the notification. |
| IronDome.Notification.dome_notification.severity_suspicious_details.domains | String | The list of domains correlated on a suspicious alert. |
| IronDome.Notification.dome_notification.severity_suspicious_details.ips | String | The list of IPs correlated on a suspicious alert. |
| IronDome.Notification.dome_notification.severity_suspicious_details.comments | String | The comments about this suspicious alert correlation. |
| IronDome.Notification.dome_notification.severity_malicious_details.domains | String | The list of domains correlated on a malicious alert. |
| IronDome.Notification.dome_notification.severity_malicious_details.ips | String | The list of IPs correlated on a malicious alert. |
| IronDome.Notification.dome_notification.severity_malicious_details.comments | String | The comments about this malicious alert correlation. |
| IronDome.Notification.dome_notification.severity_malicious_details.generated_threat_intel_rules.ip | String | The IP of a Threat Intelligence rule that was automatically created based on an IronDome correlation. |
| IronDome.Notification.dome_notification.severity_malicious_details.generated_threat_intel_rules.domain | String | The domain of a Threat Intelligence rule that was automatically created based on an IronDome correlation. |
| IronDome.Notification.dome_notification.severity_malicious_details.generated_threat_intel_rules.rule_id | String | The rule ID of a Threat Intelligence rule that was automatically created based on an IronDome correlation. |
| IronDome.Notification.dome_notification.severity_malicious_details.generated_threat_intel_rules.vue_url | String | The IronVUE URL of a Threat Intelligence rule that was automatically created based on an IronDome correlation. |
| IronDome.Notification.dome_notification.high_cognitive_system_score_details.enterprise_alert_score | Number | The alert score for the enterprise. |
| IronDome.Notification.dome_notification.high_cognitive_system_score_details.cognitive_system_score | Number | The cognitive system score. |
irondefense-get-events-from-alert
Retrieves IronDefense Events for a given Alert ID.
Base Command
irondefense-get-events-from-alert
Input
| Argument Name | Description | Required |
|---|---|---|
| alert_id | The ID of the IronDefense alert to retrieve events for. | Required |
| limit | The limit on the number of Events to be returned. | Optional |
| offset | The number of results to skip - used for paging the results. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| IronDefense.Event.id | String | The event ID. |
| IronDefense.Event.alert_id | String | The associated alert ID. |
| IronDefense.Event.category | String | The event category. |
| IronDefense.Event.sub_category | String | The event sub category. |
| IronDefense.Event.severity | Number | The event severity (0-1000). |
| IronDefense.Event.confidence | Number | The event confidence (real number between 0-1). |
| IronDefense.Event.created | Date | Time the event was created. |
| IronDefense.Event.updated | Date | Time the event was last updated. |
| IronDefense.Event.start_time | Date | The start time of this event’s activity. |
| IronDefense.Event.end_time | Date | The end time of this event’s activity. |
| IronDefense.Event.iron_dome_shared_time | Date | The time when the event was sent to IronDome - not present if not shared with irondome. |
| IronDefense.Event.is_whitelisted | Boolean | True if the event activity was whitelisted, false otherwise. |
| IronDefense.Event.is_blacklisted | Boolean | True if the event activity was blacklisted, false otherwise. |
| IronDefense.Event.src_ip | String | The source IP associated with this event. |
| IronDefense.Event.dst_ip | String | The destination IP associated with this event. |
| IronDefense.Event.dst_port | Number | The destination port associated with this event. |
| IronDefense.Event.app_domains | String | Any domains associated with this event. |
| IronDefense.Event.primary_app_protocol | String | The primary application protocol associated with this event. |
| IronDefense.Event.secondary_app_protocol | String | The secondary application protocol associated with this event. |
| IronDefense.Event.bytes_in | Number | The byte count of incoming traffic for this event. |
| IronDefense.Event.bytes_out | Number | The byte count of outgoing traffic for this event. |
| IronDefense.Event.total_bytes | Number | The byte count of the total traffic (in either direction) for this event. |
| IronDefense.Event.url | String | The related URL for this event, if applicable. |
| IronDefense.Event.raw_data_formats | String | The list of distinct raw data formats for this event. |
| IronDefense.Event.src_entity_attribute | String | The source entity attribute related to this event, if one could be determined. |
| IronDefense.Event.src_entity_attribute_type | String | The source entity attribute type related to this event, if one could be determined. |
| IronDefense.Event.dst_entity_attribute | String | The destination entity attribute related to this event, if one could be determined. |
| IronDefense.Event.dst_entity_attribute_type | String | The destination entity attribute type related to this event, if one could be determined. |
| IronDefense.Event.vue_url | String | The url for displaying the event within IronVUE. |
| IronDefense.Query.GetEvents.limit | Number | The maximum number of results that were requested to be returned. |
| IronDefense.Query.GetEvents.offset | Number | The number of results that were skipped - used for paging the results. |
| IronDefense.Query.GetEvents.total | Number | The total number of results possible from the query. |
Configuration parameters
ironAPIHost— IronAPI Host/IP (required)ironAPIPort— IronAPI Port (required)credentials— Username (required)requestTimeout— Request Timeout (Sec)isFetch— Fetch incidentsincidentType— Incident typeincidentFetchInterval— Incidents Fetch IntervalenableAlertNotifications— Enable Alert Notification Ingestion (Refresh Not Supported)alertLimit— Alert Notification Limit Per Request. (Max 500)alertCategories— Alert Notification Categories to ExcludealertSubCategories— Alert Notification SubCategories to Exclude, See https://ironnet.atlassian.net/wiki/x/hADvGg for values (Enter in comma separated list format)alertSeverityLower— Lower Bound of Severity for Alert Notification (0-1000)alertSeverityUpper— Upper Bound of Severity for Alert Notification (0-1000)alertActions— Alert Notification Actions to Include (Alert Created enabled by default)enableEventNotifications— Enable Event Notification Ingestion (Refresh Not Supported)eventLimit— Event Notification Limit Per Request. (Max 500)eventCategories— Event Notification Categories to ExcludeeventSubCategories— Event Notification SubCategories to Exclude, See https://ironnet.atlassian.net/wiki/x/hADvGg for values (Enter in comma separated list format)eventSeverityLower— Lower Bound of Severity for Event Notification (0-1000)eventSeverityUpper— Upper Bound of Severity for Event Notification (0-1000)eventActions— Event Notification Actions to Include (Event Created enabled by default)enableDomeNotifications— Enable Dome Notification Ingestion (Refresh Not Supported)domeLimit— Dome Notification Limit Per Request. (Max 500)domeCategories— Dome Notification Types to Exclude
Commands (8)
-
irondefense-comment-alertAdds a comment to an IronDefense alert.
-
irondefense-get-alert-irondome-informationRetrieves IronDome information for an IronDefense alert.
-
irondefense-get-alertsPulls Alerts from IronDefense.
-
irondefense-get-eventRetrieves an IronDefense event.
-
irondefense-get-events-from-alertRetrieves IronDefense Events for a given Alert ID.
-
irondefense-rate-alertRates an IronDefense alert.
-
irondefense-report-observed-bad-activitySubmits an observed bad endpoint to IronDefense to create Threat. Intelligence Rules (TIR).
-
irondefense-set-alert-statusSets the status of an IronDefense alert.
import demistomock as demisto # noqa: F401 from CommonServerPython import * # noqa: F401 import functools from http.client import HTTPException import urllib3 # Disable insecure warnings urllib3.disable_warnings() class XsoarLogger: """Wrapper class to add a prefix to all logging statements so they can be located more easily in the logs""" def __init__(self, d, log_prefix=""): self.demisto = d self.log_prefix = log_prefix def debug(self, msg): demisto.debug(self.log_prefix + msg) def error(self, msg): demisto.error(self.log_prefix + msg) class IronDefense: """Main class for performing plugin actions""" def __init__(self, demisto, session, host, port, credentials, logger, request_timeout=60.0): self.demisto = demisto self.session = session self.host = host self.port = port self.base_url = f"https://{host}:{port}/IronApi" self.credentials = credentials self.request_timeout = request_timeout self.logger = logger self.session.headers.update({"Content-Type": "application/json"}) self._configure_session_auth(self.demisto.getIntegrationContext()) """ HELPER FUNCTIONS """ def _get_jwt(self, context): if context is None: return None try: return context.get("JWT") except KeyError: return None def _configure_session_auth(self, data): self.logger.debug("Getting jwt...") jwt = self._get_jwt(data) if jwt: # set the auth token if it exists self.session.headers.update({"Authorization": "Bearer " + jwt}) def _http_request(self, method, uri, body="{}", headers={}, params={}, auth=None, files=None): # Makes an API call with the given arguments resp = self.session.request( method, self.base_url + uri, data=body, headers=headers, verify=False, params=params, files=files, timeout=self.request_timeout, auth=auth, ) if resp.status_code == 401: if auth is not None: # incorrect creds have been provided return resp # the session has expired so we need to log in again self.logger.debug("Login required!") username = self.credentials.get("identifier") password = self.credentials.get("password") # retry the original request with basic auth credentials return self._http_request(method, uri, body=body, params=params, files=files, auth=(username, password)) elif resp.status_code >= 500: self.logger.error("A server error has occurred. The response is: " + json.dumps(resp.json())) if auth is not None: # persist the jwt jwt = resp.headers.get("auth-token") self.demisto.setIntegrationContext({"JWT": jwt}) return resp def _get_error_msg_from_response(self, resp): err_msg = resp.json().get("msg") if err_msg is None: err_msg = resp.text return err_msg def event_context_table_to_dict_list(self, event_context_table): # convert context from column format to row format for display num_rows = functools.reduce( lambda acc, col: len(col.get("values")) if len(col.get("values")) > acc else acc, event_context_table.get("columns"), 0, ) new_table_data: list[dict[str, str]] = [{} for row in range(num_rows)] for column in event_context_table.get("columns"): # add the column to the table for row in range(num_rows): # check if the column has the row data if len(column.get("values")) - row > 0: # insert column data val = [ str(list(d.values())[0]) if len(list(d.values())) > 0 else "" for d in column.get("values")[row].get("data") ] table_row = new_table_data[row] column_name = column.get("name") table_row[column_name] = ",".join(val) else: # This column is missing data so just insert an empty string new_table_data[row][column.get("name")] = "" return new_table_data def event_context_table_to_dict(self, event_context_table): table_data = {} for column in event_context_table.get("columns"): val = [str(list(d.values())[0]) if len(list(d.values())) > 0 else "" for d in column.get("values")[0].get("data")] table_data[column.get("name")] = ",".join(val) return table_data def event_context_table_contains_multi_columns(self, event_context_table): return any(len(column.get("values")) > 1 for column in event_context_table.get("columns")) def create_markdown_link(self, link_text, url): return f"[{link_text}]({url})" def create_dome_markdown_link(self, link_text, alert_id): url = f"https://{self.host}/alerts/irondome?filter=alertId%3D%3D{alert_id}" return f"[{link_text}]({url})" """MAIN FUNCTIONS""" def fetch_dome_incidents(self, dome_categories=None, dome_limit=200): self.logger.debug("Fetching Dome incidents...") res = [] if dome_categories is not None: dome_cats = ["DNC_" + str(cat).replace(" ", "_").upper() for cat in dome_categories] else: dome_cats = [] req_body = json.dumps({"limit": dome_limit}) resp = self._http_request("POST", "/GetDomeNotifications", body=req_body) if resp.ok: # Filter notifications notifs = resp.json() self.logger.debug("json response is: " + json.dumps(resp.json())) for n in notifs["dome_notifications"]: if n["category"] not in dome_cats: n["type"] = "dome" notif = { "name": str(n["category"]) + " IronDome Notification", "details": "Received a {} IronDome Notification at {} from communities {}.".format( n["category"], str(datetime.now()), n["dome_tags"] ), "occurred": n["created"], "rawJSON": json.dumps(n), } res.append(notif) else: raise Exception("Fetch for DomeNotifications failed. Status code was " + str(resp.status_code)) self.logger.debug(f"{len(res)} Dome incident(s) fetched") return res def fetch_alert_incidents( self, alert_categories=None, alert_subcategories=None, alert_severity_lower=None, alert_severity_upper=None, alert_limit=200, alert_actions=None, ): self.logger.debug("Fetching Alert incidents...") res = [] if alert_categories is not None: alert_cats = [str(cat).replace(" ", "_").upper() for cat in alert_categories] else: alert_cats = [] if alert_subcategories is not None: asc = alert_subcategories.split(",") alert_subcats = [str(subcat).replace(" ", "_").upper() for subcat in asc] else: alert_subcats = [] if alert_actions is None or len(alert_actions) == 0: alert_actions_to_ingest = ["ANA_ALERT_CREATED"] else: alert_actions_to_ingest = ["ANA_" + str(alert_action).replace(" ", "_").upper() for alert_action in alert_actions] alert_sev_lower = int(alert_severity_lower) if alert_severity_lower is not None else 0 alert_sev_upper = int(alert_severity_upper) if alert_severity_upper is not None else 1000 req_body = json.dumps({"limit": alert_limit}) resp = self._http_request("POST", "/GetAlertNotifications", body=req_body) if resp.ok: # Filter notifications notifs = resp.json() self.logger.debug("json response is: " + json.dumps(resp.json())) for alert_notification in notifs["alert_notifications"]: if alert_notification["alert"]: alert = alert_notification["alert"] action = alert_notification["alert_action"] if ( alert["category"] not in alert_cats and alert["sub_category"] not in alert_subcats and alert_sev_lower <= int(alert["severity"]) <= alert_sev_upper and action in alert_actions_to_ingest ): alert["type"] = "alert" notif = { "name": str(alert_notification["alert_action"]) + " Alert Notification", "details": "Received a {} Alert Notification at {}.".format( alert_notification["alert_action"], str(datetime.now()) ), "occurred": alert["updated"], "rawJSON": json.dumps(alert), } res.append(notif) else: raise Exception("Fetch for AlertNotifications failed. Status code was " + str(resp.status_code)) self.logger.debug(f"{len(res)} Alert incident(s) fetched") return res def fetch_event_incidents( self, event_categories=None, event_subcategories=None, event_severity_lower=None, event_severity_upper=None, event_limit=200, event_actions=None, ): self.logger.debug("Fetching Event incidents...") res = [] if event_categories is not None: event_cats = [str(cat).replace(" ", "_").upper() for cat in event_categories] else: event_cats = [] if event_subcategories is not None: esc = event_subcategories.split(",") event_subcats = [str(subcat).replace(" ", "_").upper() for subcat in esc] else: event_subcats = [] if event_actions is None or len(event_actions) == 0: event_actions_to_ingest = ["ENA_EVENT_CREATED"] else: event_actions_to_ingest = ["ENA_" + str(event_action).replace(" ", "_").upper() for event_action in event_actions] event_sev_lower = int(event_severity_lower) if event_severity_lower is not None else 0 event_sev_upper = int(event_severity_upper) if event_severity_upper is not None else 1000 req_body = json.dumps({"limit": event_limit}) resp = self._http_request("POST", "/GetEventNotifications", body=req_body) if resp.ok: # Filter notifications notifs = resp.json() self.logger.debug("json response is: " + json.dumps(resp.json())) for event_notification in notifs["event_notifications"]: if event_notification["event"]: event = event_notification["event"] action = event_notification["event_action"] if ( event["category"] not in event_cats and event["sub_category"] not in event_subcats and event_sev_lower <= int(event["severity"]) <= event_sev_upper and action in event_actions_to_ingest ): event["type"] = "event" notif = { "name": str(event_notification["event_action"]) + " Event Notification", "details": "Received a {} Event Notification at {}.".format( event_notification["event_action"], str(datetime.now()) ), "occurred": event["updated"], "rawJSON": json.dumps(event), } res.append(notif) else: raise Exception("Fetch for EventNotifications failed. Status code was " + str(resp.status_code)) self.logger.debug(f"{len(res)} Event incident(s) fetched") return res def test_module(self): self.logger.debug("Testing module...") username = self.credentials.get("identifier") password = self.credentials.get("password") resp = self._http_request("POST", "/Login", auth=(username, password)) if resp.status_code == 200: self.logger.debug("Success!") return "ok" else: return "Test failed ({}): {}".format(str(resp.status_code), resp.json()["msg"]) def update_analyst_ratings( self, alert_id, severity="SEVERITY_UNDECIDED", expectation="EXP_UNKNOWN", comments="", share_irondome=False ): self.logger.debug( f"Submitting analyst rating: Alert ID={alert_id} Severity={severity} Expected={expectation} " f"Comments={comments} Share w/IronDome={share_irondome}" ) req_body = { "alert_id": alert_id, "analyst_severity": "SEVERITY_" + severity.upper(), "analyst_expectation": "EXP_" + expectation.upper(), "comment": comments, "share_comment_with_irondome": share_irondome, } response = self._http_request("POST", "/RateAlert", body=json.dumps(req_body)) if response.status_code != 200: err_msg = self._get_error_msg_from_response(response) self.logger.error( f"Failed to rate alert ({alert_id}). The response failed with status code {response.status_code}." f" The response was: {response.text}" ) raise HTTPException(f"Failed to rate alert {alert_id} ({response.status_code}): {err_msg}") else: self.logger.debug(f"Successfully submitted rating for alert ({alert_id})") return "Submitted analyst rating to IronDefense!" def add_comment_to_alert(self, alert_id, comment="", share_irondome=False): self.logger.debug(f"Submitting comment: Alert ID={alert_id} Comment={comment} Share w/IronDome={share_irondome}") req_body = {"alert_id": alert_id, "comment": comment, "share_comment_with_irondome": share_irondome} response = self._http_request("POST", "/CommentOnAlert", body=json.dumps(req_body)) if response.status_code != 200: err_msg = self._get_error_msg_from_response(response) self.logger.error( f"Failed to add comment to alert ({alert_id}). The response failed with status code {response.status_code}." f" The response was: {response.text}" ) raise HTTPException(f"Failed to add comment to alert {alert_id} ({response.status_code}): {err_msg}") else: self.logger.debug(f"Successfully added comment to alert ({alert_id})") return "Submitted comment to IronDefense!" def set_alert_status(self, alert_id, status="STATUS_NONE", comments="", share_irondome=False): self.logger.debug( f"Submitting status: Alert ID={alert_id} Status={status} Comments={comments} Share w/IronDome={share_irondome}" ) req_body = { "alert_id": alert_id, "status": "STATUS_" + status.upper().replace(" ", "_"), "comment": comments, "share_comment_with_irondome": share_irondome, } response = self._http_request("POST", "/SetAlertStatus", body=json.dumps(req_body)) if response.status_code != 200: err_msg = self._get_error_msg_from_response(response) self.logger.error( f"Failed to set status for alert ({alert_id}). The response failed with status code {response.status_code}." f" The response was: {response.text}" ) raise HTTPException(f"Failed to set status for alert {alert_id} ({response.status_code}): {err_msg}") else: self.logger.debug(f"Successfully submitted status for alert ({alert_id})") return "Submitted status to IronDefense!" def report_observed_bad_activity( self, name, description="", ip="", domain="", activity_start_time="1970-01-01T00:00:00Z", activity_end_time="1970-01-01T00:00:00Z", ): self.logger.debug( f"Submitting observed bad activity: Name={name} Description={description} IP={ip} Domain={domain} " f"Activity Start Time={activity_start_time} Activity End Time={activity_end_time}" ) req_body = { "name": name, "description": description, "ip": ip, "domain": domain, "activity_start_time": activity_start_time, "activity_end_time": activity_end_time, } response = self._http_request("POST", "/ReportObservedBadActivity", body=json.dumps(req_body)) if response.ok: self.logger.debug(f"Successfully submitted observed bad activity for IP={ip} and Domain={domain}") return "Submitted observed bad activity to IronDefense!" else: err_msg = self._get_error_msg_from_response(response) self.logger.error( f"Failed to submit observed bad activity for IP={ip} and Domain={domain}. The response failed with" f" status code {response.status_code}. The response was: {response.text}" ) raise HTTPException( f"Failed to submit observed bad activity for IP={ip} and Domain={domain} ({response.status_code}): {err_msg}" ) def get_event(self, event_id): self.logger.debug(f"Retrieving Event: Event ID={event_id}") req_body = { "event_id": event_id, } response = self._http_request("POST", "/GetEvent", body=json.dumps(req_body)) if response.status_code != 200: err_msg = self._get_error_msg_from_response(response) self.logger.error( f"Failed to retrieve event with ID ({event_id}). The response failed with status code {response.status_code}." f" The response was: {response.text}" ) raise HTTPException(f"Failed to retrieve event with ID {event_id} ({response.status_code}): {err_msg}") else: self.logger.debug(f"Successfully retrieved event ({event_id})") return response.json() def get_events(self, alert_id, limit=None, offset=None): self.logger.debug(f"Retrieving Events: Alert ID={alert_id}, Limit={limit} Offset={offset}") req_body = {"alert_id": alert_id} constraint = {} if limit is not None and limit != "": constraint["limit"] = int(limit) if offset is not None and offset != "": constraint["offset"] = int(offset) req_body["constraint"] = constraint response = self._http_request("POST", "/GetEvents", body=json.dumps(req_body)) if response.status_code != 200: err_msg = self._get_error_msg_from_response(response) self.logger.error( f"Failed to retrieve events with alert ID ({alert_id}). The response failed with status code" f" {response.status_code}. " f"The response was: {response.text}" ) raise HTTPException(f"Failed to retrieve event with ID {alert_id} ({response.status_code}): {err_msg}") else: self.logger.debug(f"Successfully retrieved events for alert ({alert_id})") events = response.json() return events def get_alerts( self, alert_id=None, category=None, sub_category=None, status=None, analyst_severity=None, analyst_expectation=None, min_severity=None, max_severity=None, min_created=None, max_created=None, min_updated=None, max_updated=None, min_first_event_created=None, max_first_event_created=None, min_last_event_created=None, max_last_event_created=None, min_first_event_start_time=None, max_first_event_start_time=None, min_last_event_end_time=None, max_last_event_end_time=None, analytic_version=None, limit=None, offset=None, sort=None, ): self.logger.debug( f"Getting alerts: AlertID={alert_id} Category={category} SubCategory={sub_category} Status={status}" f" AnalystSeverity={analyst_severity} " f"AnalystExpectation={analyst_expectation} MinSeverity={min_severity} MaxSeverity={max_severity} " f"MinCreated={min_created} MaxCreated= {max_created} MinUpdated={min_updated}" f"MaxUpdated={max_updated} MinFirstEventCreated={min_first_event_created} " f"MaxFirstEventCreated={max_first_event_created} MinLastEventCreated={min_last_event_created}" f"MaxLastEventCreated={max_last_event_created} MinFirstEventStartTime={min_first_event_start_time}" f" MaxFirstEventStartTime={max_first_event_start_time} MinLastEventEndTime={min_last_event_end_time}" f"MaxLastEventEndTime={max_last_event_end_time} AnalyticVersion={analytic_version} " f"Limit={limit} Offset={offset} sort={sort}" ) req_body = {} if alert_id: req_body["alert_id"] = alert_id.split(",") if category: req_body["category"] = [str(cat).replace(" ", "_").upper() for cat in category.split(",")] if sub_category: req_body["sub_category"] = [str(sub_cat).replace(" ", "_").upper() for sub_cat in sub_category.split(",")] if status: req_body["status"] = ["STATUS_" + str(stat).replace(" ", "_").upper() for stat in status.split(",")] if analyst_severity: req_body["analyst_severity"] = [ "SEVERITY_" + str(aseverity).replace(" ", "_").upper() for aseverity in analyst_severity.split(",") ] if analyst_expectation: req_body["analyst_expectation"] = [ "EXP_" + str(aexpectation).replace(" ", "_").upper() for aexpectation in analyst_expectation.split(",") ] if analytic_version: req_body["analytic_version"] = analytic_version.split(",") if sort: req_body["sort"] = sort if min_severity is not None and min_severity != "" and max_severity is not None and max_severity != "": req_body["severity"] = {"lower_bound": int(min_severity), "upper_bound": int(max_severity)} if min_created and max_created: req_body["created"] = {"start": min_created, "end": max_created} if min_updated and max_updated: req_body["updated"] = {"start": min_updated, "end": max_updated} if min_first_event_created and max_first_event_created: req_body["first_event_created"] = {"start": min_first_event_created, "end": max_first_event_created} if min_last_event_created and max_last_event_created: req_body["last_event_created"] = {"start": min_last_event_created, "end": max_last_event_created} if min_first_event_start_time and max_first_event_start_time: req_body["first_event_start_time"] = {"start": min_first_event_start_time, "end": max_first_event_start_time} if min_last_event_end_time and max_last_event_end_time: req_body["last_event_end_time"] = {"start": min_last_event_end_time, "end": max_last_event_end_time} constraint = {} if limit is not None and limit != "": constraint["limit"] = int(limit) if offset is not None and offset != "": constraint["offset"] = int(offset) req_body["constraint"] = constraint response = self._http_request("POST", "/GetAlerts", body=json.dumps(req_body)) if response.ok: self.logger.debug("Successfully retrieved alerts") return response.json() else: err_msg = self._get_error_msg_from_response(response) self.logger.error( f"Failed to retrieve alerts. The response failed with status code {response.status_code}." f" The response was: {err_msg}" ) raise HTTPException(f"Failed to retrieve alerts ({response.status_code}): {err_msg}") def get_alert_irondome_information(self, alert_id): self.logger.debug(f"Retrieving Alert IronDome Information: Alert ID={alert_id}") req_body = { "alert_id": alert_id, } response = self._http_request("POST", "/GetAlertIronDomeInformation", body=json.dumps(req_body)) if response.status_code != 200: err_msg = self._get_error_msg_from_response(response) self.logger.error( f"Failed to retrieve IronDome information for alert with ID ({alert_id}). The response failed " f"with status code {response.status_code}. The response was: {response.text}" ) raise HTTPException( f"Failed to retrieve IronDome information for alert with ID {alert_id} ({response.status_code}): {err_msg}" ) else: self.logger.debug(f"Successfully retrieved IronDome information for alert ({alert_id})") dome_alert_info = response.json() return dome_alert_info """ COMMANDS MANAGER / SWITCH PANEL """ def fetch_incidents_command(): # IronDome Notification related params dome_categories = PARAMS.get("domeCategories", None) # pylint: disable=E0601 dome_limit = int(PARAMS.get("domeLimit", 200)) disable_dome_notifs = not PARAMS.get("enableDomeNotifications", False) # Alert Notification related params alert_categories = PARAMS.get("alertCategories", None) alert_subcategories = PARAMS.get("alertSubCategories", None) alert_severity_lower = PARAMS.get("alertSeverityLower", None) alert_severity_upper = PARAMS.get("alertSeverityUpper", None) alert_limit = int(PARAMS.get("alertLimit", 200)) alert_actions = PARAMS.get("alertActions", None) disable_alert_notifs = not PARAMS.get("enableAlertNotifications", True) # Event Notification related params event_categories = PARAMS.get("eventCategories", None) event_subcategories = PARAMS.get("eventSubCategories", None) event_severity_lower = PARAMS.get("eventSeverityLower", None) event_severity_upper = PARAMS.get("eventSeverityUpper", None) event_limit = int(PARAMS.get("eventLimit", 200)) event_actions = PARAMS.get("eventActions", None) disable_event_notifs = not PARAMS.get("enableEventNotifications", False) incidents: list = [] if disable_dome_notifs and disable_alert_notifs and disable_event_notifs: LOGGER.debug("Ingestion of all notifications (Dome, Alert, Event) is disabled, not fetching") # pylint: disable=E0601 else: if disable_dome_notifs: LOGGER.debug("Ingestion of Dome Notifications is disabled") else: incs = IRON_DEFENSE.fetch_dome_incidents(dome_categories, dome_limit) # pylint: disable=E0601 incidents.extend(incs) # If the limit was reached, poll again poll_count = 1 while len(incs) == dome_limit and poll_count < 10: incs = IRON_DEFENSE.fetch_dome_incidents(dome_categories, dome_limit) incidents.extend(incs) poll_count += 1 if disable_alert_notifs: LOGGER.debug("Ingestion of Alert Notifications is disabled") else: incs = IRON_DEFENSE.fetch_alert_incidents( alert_categories, alert_subcategories, alert_severity_lower, alert_severity_upper, alert_limit, alert_actions ) incidents.extend(incs) # If the limit was reached, poll again poll_count = 1 while len(incs) == alert_limit and poll_count < 10: incs = IRON_DEFENSE.fetch_alert_incidents( alert_categories, alert_subcategories, alert_severity_lower, alert_severity_upper, alert_limit, alert_actions ) incidents.extend(incs) poll_count += 1 if disable_event_notifs: LOGGER.debug("Ingestion of Event Notifications is disabled") else: incs = IRON_DEFENSE.fetch_event_incidents( event_categories, event_subcategories, event_severity_lower, event_severity_upper, event_limit, event_actions ) incidents.extend(incs) # If the limit was reached, poll again poll_count = 1 while len(incs) == event_limit and poll_count < 10: incs = IRON_DEFENSE.fetch_event_incidents( event_categories, event_subcategories, event_severity_lower, event_severity_upper, event_limit, event_actions ) incidents.extend(incs) poll_count += 1 demisto.incidents(incidents) def test_module_command(): results = IRON_DEFENSE.test_module() demisto.results(results) def update_analyst_ratings_command(): alert_id = demisto.getArg("alert_id") severity = demisto.getArg("severity") expectation = demisto.getArg("expectation") comments = demisto.getArg("comments") share_irondome_arg = argToBoolean(demisto.getArg("share_comment_with_irondome")) results = IRON_DEFENSE.update_analyst_ratings( alert_id, severity=severity, expectation=expectation, comments=comments, share_irondome=share_irondome_arg ) demisto.results(results) def add_comment_to_alert_command(): alert_id = demisto.getArg("alert_id") comment = demisto.getArg("comment") share_irondome_arg = argToBoolean(demisto.getArg("share_comment_with_irondome")) results = IRON_DEFENSE.add_comment_to_alert(alert_id, comment=comment, share_irondome=share_irondome_arg) demisto.results(results) def set_alert_status_command(): alert_id = demisto.getArg("alert_id") status = demisto.getArg("status") comments = demisto.getArg("comments") share_irondome_arg = argToBoolean(demisto.getArg("share_comment_with_irondome")) results = IRON_DEFENSE.set_alert_status(alert_id, status=status, comments=comments, share_irondome=share_irondome_arg) demisto.results(results) def report_observed_bad_activity_command(): name = demisto.getArg("name") description = demisto.getArg("description") ip = demisto.getArg("ip") domain = demisto.getArg("domain") activity_start_time = demisto.getArg("activity_start_time") activity_end_time = demisto.getArg("activity_end_time") results = IRON_DEFENSE.report_observed_bad_activity( name, description=description, ip=ip, domain=domain, activity_start_time=activity_start_time, activity_end_time=activity_end_time, ) demisto.results(results) def get_event_command(): # get event data from IronAPI event_id = demisto.getArg("event_id") results = IRON_DEFENSE.get_event(event_id) # Output the event data event = results.get("event") vue_markdown_link = IRON_DEFENSE.create_markdown_link("Open in IronVue", event.get("vue_url")) event_readable_output = tableToMarkdown( f'IronDefense Event: {event.get("category")} - {event.get("sub_category")}\n{vue_markdown_link}', event ) return_outputs( readable_output=event_readable_output, outputs={ "IronDefense.Event(val.id == obj.id)": event, }, raw_response=event, ) # Output each context table context_tables = results.get("context") for table in context_tables: if IRON_DEFENSE.event_context_table_contains_multi_columns(table): output_table = IRON_DEFENSE.event_context_table_to_dict_list(table) headers = [*output_table[0]] else: output_table = IRON_DEFENSE.event_context_table_to_dict(table) headers = [] return_outputs( readable_output=tableToMarkdown(f'Event Context: {table.get("name")}', output_table, headers=headers), outputs={ "IronDefense.Event.Context(val.name == obj.name)": table, }, raw_response=table, ) def get_events_command(): alert_id = demisto.getArg("alert_id") limit = demisto.getArg("limit") offset = demisto.getArg("offset") results = IRON_DEFENSE.get_events(alert_id=alert_id, limit=limit, offset=offset) events = results.get("events") total_count = results.get("constraint").get("total") offset = results.get("constraint").get("offset") for i, event in enumerate(events): vue_markdown_link = IRON_DEFENSE.create_markdown_link("Open in IronVue", event.get("vue_url")) event_readable_output = tableToMarkdown(f"IronDefense Event {i + offset + 1}/{total_count}\n{vue_markdown_link}", event) # Send each event return_outputs( readable_output=event_readable_output, outputs={ "IronDefense.Event(val.id == obj.id)": event, }, raw_response=event, ) # Send constraints constraint = results.get("constraint") return_outputs( readable_output=tableToMarkdown("Query Constraints", constraint), outputs={ "IronDefense.Query.GetEvents": constraint, }, raw_response=constraint, ) def get_alerts_command(): alert_id = demisto.getArg("alert_id") category = demisto.getArg("category") sub_category = demisto.getArg("sub_category") status = demisto.getArg("status") analyst_severity = demisto.getArg("analyst_severity") analyst_expectation = demisto.getArg("analyst_expectation") min_severity = demisto.getArg("min_severity") max_severity = demisto.getArg("max_severity") min_created = demisto.getArg("min_created") max_created = demisto.getArg("max_created") min_updated = demisto.getArg("min_updated") max_updated = demisto.getArg("max_updated") min_first_event_created = demisto.getArg("min_first_event_created") max_first_event_created = demisto.getArg("max_first_event_created") min_last_event_created = demisto.getArg("min_last_event_created") max_last_event_created = demisto.getArg("max_last_event_created") min_first_event_start_time = demisto.getArg("min_first_event_start_time") max_first_event_start_time = demisto.getArg("max_first_event_start_time") min_last_event_end_time = demisto.getArg("min_last_event_end_time") max_last_event_end_time = demisto.getArg("max_last_event_end_time") analytic_version = demisto.getArg("analytic_version") limit = demisto.getArg("limit") offset = demisto.getArg("offset") sort = demisto.getArg("sort") results = IRON_DEFENSE.get_alerts( alert_id=alert_id, category=category, sub_category=sub_category, status=status, analyst_severity=analyst_severity, analyst_expectation=analyst_expectation, min_severity=min_severity, max_severity=max_severity, min_created=min_created, max_created=max_created, min_updated=min_updated, max_updated=max_updated, min_first_event_created=min_first_event_created, max_first_event_created=max_first_event_created, min_last_event_created=min_last_event_created, max_last_event_created=max_last_event_created, min_first_event_start_time=min_first_event_start_time, max_first_event_start_time=max_first_event_start_time, min_last_event_end_time=min_last_event_end_time, max_last_event_end_time=max_last_event_end_time, analytic_version=analytic_version, limit=limit, offset=offset, sort=sort, ) alerts = results.get("alerts") total_count = results.get("constraint").get("total") offset = results.get("constraint").get("offset") for i, alert in enumerate(alerts): # Send each alert vue_markdown_link = IRON_DEFENSE.create_markdown_link("Open in IronVue", alert.get("vue_url")) alert_readable_output = tableToMarkdown( f'IronDefense Alert {i + offset + 1}/{total_count}: {alert.get("category")} -' f' {alert.get("sub_category")}\n' f'{vue_markdown_link}', alert, ) return_outputs( readable_output=alert_readable_output, outputs={ "IronDefense.Alert(val.id == obj.id)": alert, }, raw_response=alert, ) # Send constraints constraint = results.get("constraint") return_outputs( readable_output=tableToMarkdown("Query Constraints", constraint), outputs={ "IronDefense.Query.GetAlerts": constraint, }, raw_response=constraint, ) def get_alert_irondome_information_command(): alert_id = demisto.getArg("alert_id") results = IRON_DEFENSE.get_alert_irondome_information(alert_id) if ( len(results.get("correlations")) == 0 and len(results.get("correlation_participation")) == 0 and len(results.get("community_comments")) == 0 and len(results.get("dome_notifications")) == 0 ): demisto.results(f"No correlations found for alert ID: {alert_id}") return # Output correlations correlations = results.get("correlations") for correlation in correlations: dome_tag = correlation.get("dome_tag") correlation_data = correlation.get("correlations") output = {"alert_id": alert_id, "correlation": correlation} ip_correlations = list(filter(lambda corr: corr.get("ip") is not None, correlation_data)) domain_correlations = list(filter(lambda corr: corr.get("domain") is not None, correlation_data)) behavior_correlations = list(filter(lambda corr: corr.get("behavior") is not None, correlation_data)) if len(ip_correlations) != 0: return_outputs( readable_output=tableToMarkdown( f'IronDome IP Correlations in "{dome_tag}"', ip_correlations, headers=[*ip_correlations[0]] ), outputs={"IronDome.Correlations(val.alert_id = obj.alert.id)": output}, raw_response=correlation, ) if len(domain_correlations) != 0: return_outputs( readable_output=tableToMarkdown( f'IronDome Domain Correlations in "{dome_tag}"', domain_correlations, headers=[*domain_correlations[0]] ), outputs={"IronDome.Correlations(val.alert_id = obj.alert.id)": output}, raw_response=correlation, ) if len(behavior_correlations) != 0: return_outputs( readable_output=tableToMarkdown( f'IronDome Behavior Correlations in "{dome_tag}"', behavior_correlations, headers=[*behavior_correlations[0]] ), outputs={"IronDome.Correlations(val.alert_id = obj.alert.id)": output}, raw_response=correlation, ) # Output correlation participation correlation_participation = results.get("correlation_participation") for participant in correlation_participation: dome_tag = participant.get("dome_tag") output = {"alert_id": alert_id, "correlation_participation": participant} table_data = [] # append each correlation context to display in the table, if it exists behavior = participant.get("behavior") if behavior is not None: table_data.append(behavior) domain = participant.get("behavior") if domain is not None: table_data.append(domain) ip = participant.get("ip") if ip is not None: table_data.append(ip) # Send the participant info return_outputs( readable_output=tableToMarkdown( f'IronDome Correlation Participation in "{dome_tag}"', table_data, headers=[*table_data[0]] ), outputs={"IronDome.CorrelationParticipation(val.alert_id = obj.alert.id)": output}, raw_response=participant, ) # Output comments community_comments = results.get("community_comments") community_comments_output = { "alert_id": alert_id, "community_comments": community_comments, } if len(community_comments) > 0: return_outputs( readable_output=tableToMarkdown("IronDome Community Comments", community_comments, headers=[*community_comments[0]]), outputs={"IronDome.CommunityComments(val.alert_id = obj.alert.id)": community_comments_output}, raw_response=community_comments, ) # Output cognitive system score cognitive_system_score = results.get("cognitive_system_score") cognitive_system_score_output = { "alert_id": alert_id, "cognitive_system_score": cognitive_system_score, } return_outputs( readable_output=f"### Cognitive System Score: {cognitive_system_score}", outputs={"IronDome.CognitiveSystemScore(val.alert_id = obj.alert.id)": cognitive_system_score_output}, raw_response=cognitive_system_score, ) # Output dome notifications dome_notifications = results.get("dome_notifications") for notification in dome_notifications: category = notification.get("category") output = {"alert_id": alert_id, "dome_notification": notification} return_outputs( readable_output=tableToMarkdown(f"IronDome Notification: {category}", notification), outputs={"IronDome.Notification(val.alert_id = obj.alert.id)": output}, raw_response=notification, ) return_outputs( readable_output=IRON_DEFENSE.create_dome_markdown_link("Open IronDome information in IronVue", alert_id), outputs={} ) COMMANDS = { "test-module": test_module_command, "fetch-incidents": fetch_incidents_command, "irondefense-rate-alert": update_analyst_ratings_command, "irondefense-comment-alert": add_comment_to_alert_command, "irondefense-set-alert-status": set_alert_status_command, "irondefense-report-observed-bad-activity": report_observed_bad_activity_command, "irondefense-get-event": get_event_command, "irondefense-get-events-from-alert": get_events_command, "irondefense-get-alerts": get_alerts_command, "irondefense-get-alert-irondome-information": get_alert_irondome_information_command, } COOKIE_KEY = "user_sid" LOG_PREFIX = "IronDefense Integration: " """EXECUTION""" if __name__ == "builtins": try: # Globals PARAMS = demisto.params() CREDENTIALS = PARAMS.get("credentials") HOST = PARAMS.get("ironAPIHost", "localhost") PORT = PARAMS.get("ironAPIPort", 443) REQUEST_TIMEOUT = float(PARAMS.get("requestTimeout", 60)) LOGGER = XsoarLogger(demisto, LOG_PREFIX) # initialize the IronDefense object IRON_DEFENSE = IronDefense(demisto, requests.Session(), HOST, PORT, CREDENTIALS, LOGGER, request_timeout=REQUEST_TIMEOUT) LOGGER.debug("Invoking integration with Command: " + demisto.command()) if demisto.command() in COMMANDS: COMMANDS[demisto.command()]() else: return_error("Command not found: " + demisto.command()) except Exception as e: demisto.error(traceback.format_exc()) return_error(str(e))