diff --git a/Packs/CortexXDR/Integrations/CortexXDRIR/CortexXDRIR.py b/Packs/CortexXDR/Integrations/CortexXDRIR/CortexXDRIR.py
index 954616acfd25..8cb99d50e5ae 100644
--- a/Packs/CortexXDR/Integrations/CortexXDRIR/CortexXDRIR.py
+++ b/Packs/CortexXDR/Integrations/CortexXDRIR/CortexXDRIR.py
@@ -50,6 +50,14 @@
XDR_OPEN_STATUS_TO_XSOAR = ["under_investigation", "new"]
+BIOC_AND_CR_SEVERITY_MAPPING = {
+ "info": "SEV_010_INFO",
+ "low": "SEV_020_LOW",
+ "medium": "SEV_030_MEDIUM",
+ "high": "SEV_040_HIGH",
+ "critical": "SEV_050_CRITICAL",
+}
+
def convert_epoch_to_milli(timestamp):
if timestamp is None:
@@ -563,6 +571,54 @@ def update_alerts_in_xdr_request(self, alerts_ids, severity, status, comment) ->
raise DemistoException(f"Parse Error. Response not in format, can't find reply key. The response {response}.")
return response["reply"]["alerts_ids"]
+ def get_biocs(self, request_data: dict):
+ reply = self._http_request(
+ method="POST",
+ url_suffix="/bioc/get",
+ json_data=request_data,
+ )
+ return reply
+
+ def insert_or_update_biocs(self, request_data):
+ reply = self._http_request(
+ method="POST",
+ url_suffix="/bioc/insert",
+ json_data=request_data,
+ )
+ return reply
+
+ def delete_biocs(self, request_data: dict):
+ reply = self._http_request(
+ method="POST",
+ url_suffix="/bioc/delete",
+ json_data=request_data,
+ )
+ return reply
+
+ def get_correlation_rules(self, request_data: dict):
+ reply = self._http_request(
+ method="POST",
+ url_suffix="/correlations/get",
+ json_data=request_data,
+ )
+ return reply
+
+ def create_or_update_correlation_rules(self, request_data: dict):
+ reply = self._http_request(
+ method="POST",
+ url_suffix="/correlations/insert",
+ json_data=request_data,
+ )
+ return reply
+
+ def delete_correlation_rules(self, request_data: dict):
+ reply = self._http_request(
+ method="POST",
+ url_suffix="/correlations/delete",
+ json_data=request_data,
+ )
+ return reply
+
def get_asset(self, asset_id: str):
try:
res = self._http_request(
@@ -1529,6 +1585,422 @@ def update_alerts_in_xdr_command(client: Client, args: Dict) -> CommandResults:
return CommandResults(readable_output="Alerts with IDs {} have been updated successfully.".format(",".join(array_of_all_ids)))
+def create_filters_for_bioc_and_correlation_rules(args: dict) -> list:
+ """
+ Creates a list of filters for BIOC and correlation rules based on the provided arguments.
+
+ Args:
+ args (dict): The command arguments containing filter criteria.
+
+ Returns:
+ list: A list of filter dictionaries compatible with the Cortex XDR API.
+ """
+ filters = []
+ if name := args.get("name"):
+ filters.append({"field": "name", "operator": "EQ", "value": name})
+ if severity := args.get("severity"):
+ filters.append({"field": "severity", "operator": "EQ", "value": BIOC_AND_CR_SEVERITY_MAPPING.get(severity, severity)})
+ if bioc_type := args.get("type"):
+ filters.append({"field": "type", "operator": "EQ", "value": bioc_type})
+ if is_xql := args.get("is_xql"):
+ filters.append({"field": "is_xql", "operator": "EQ", "value": argToBoolean(is_xql)})
+ if comment := args.get("comment"):
+ filters.append({"field": "comment", "operator": "EQ", "value": comment})
+ if status := args.get("status"):
+ filters.append({"field": "status", "operator": "EQ", "value": status})
+ if indicator := args.get("indicator"):
+ filters.append({"field": "indicator", "operator": "EQ", "value": indicator})
+ if mitre_technique := argToList(args.get("mitre_technique_id_and_name")):
+ filters.append({"field": "mitre_technique_id_and_name", "operator": "EQ", "value": mitre_technique})
+ if mitre_tactic := argToList(args.get("mitre_tactic_id_and_name")):
+ filters.append({"field": "mitre_tactic_id_and_name", "operator": "EQ", "value": mitre_tactic})
+ if xql_query := args.get("xql_query"):
+ filters.append({"field": "xql_query", "operator": "EQ", "value": xql_query})
+ if is_enabled := args.get("is_enabled"):
+ filters.append({"field": "is_enabled", "operator": "EQ", "value": argToBoolean(is_enabled)})
+ if description := args.get("description"):
+ filters.append({"field": "description", "operator": "EQ", "value": description})
+ if alert_name := args.get("alert_name"):
+ filters.append({"field": "alert_name", "operator": "EQ", "value": alert_name})
+ if alert_category := args.get("alert_category"):
+ filters.append({"field": "alert_category", "operator": "EQ", "value": alert_category})
+ if alert_description := args.get("alert_description"):
+ filters.append({"field": "alert_description", "operator": "EQ", "value": alert_description})
+ if alert_fields := argToList(args.get("alert_fields")):
+ filters.append({"field": "alert_fields", "operator": "EQ", "value": alert_fields})
+ if execution_mode := args.get("execution_mode"):
+ filters.append({"field": "execution_mode", "operator": "EQ", "value": execution_mode})
+ if search_window := args.get("search_window"):
+ filters.append({"field": "search_window", "operator": "EQ", "value": search_window})
+ if simple_schedule := args.get("schedule"):
+ filters.append({"field": "schedule", "operator": "EQ", "value": simple_schedule})
+ if timezone := args.get("timezone"):
+ filters.append({"field": "timezone", "operator": "EQ", "value": timezone})
+ if crontab := args.get("schedule_linux"):
+ filters.append({"field": "schedule_linux", "operator": "EQ", "value": crontab})
+ if suppression_enabled := args.get("suppression_enabled"):
+ filters.append({"field": "suppression_enabled", "operator": "EQ", "value": argToBoolean(suppression_enabled)})
+ if suppression_duration := args.get("suppression_duration"):
+ filters.append({"field": "suppression_duration", "operator": "EQ", "value": suppression_duration})
+ if suppression_fields := args.get("suppression_fields"):
+ filters.append({"field": "suppression_fields", "operator": "EQ", "value": suppression_fields})
+ if dataset := args.get("dataset"):
+ filters.append({"field": "dataset", "operator": "EQ", "value": dataset})
+ if user_defined_severity := args.get("user_defined_severity"):
+ filters.append({"field": "user_defined_severity", "operator": "EQ", "value": user_defined_severity})
+ if user_defined_category := args.get("user_defined_category"):
+ filters.append({"field": "user_defined_category", "operator": "EQ", "value": user_defined_category})
+ if mitre_defs := args.get("mitre_defs_json"):
+ try:
+ mitre_defs_json = json.loads(mitre_defs)
+ except ValueError:
+ raise DemistoException("Unable to parse 'mitre_defs'. Please use the JSON format.")
+ filters.append({"field": "mitre_defs", "operator": "EQ", "value": mitre_defs_json})
+ if investigation_query_link := args.get("investigation_query_link"):
+ filters.append({"field": "investigation_query_link", "operator": "EQ", "value": investigation_query_link})
+ if drilldown_query_timeframe := args.get("drilldown_query_timeframe"):
+ filters.append({"field": "drilldown_query_timeframe", "operator": "EQ", "value": drilldown_query_timeframe})
+ if mapping_strategy := args.get("mapping_strategy"):
+ filters.append({"field": "mapping_strategy", "operator": "EQ", "value": mapping_strategy})
+ if alert_domain := args.get("alert_domain"):
+ filters.append({"field": "alert_domain", "operator": "EQ", "value": alert_domain})
+ return filters
+
+
+def bioc_list_command(client: Client, args: Dict) -> CommandResults:
+ """
+ API Docs https://docs-cortex.paloaltonetworks.com/r/Cortex-XDR-Platform-APIs/Get-BIOCs
+ Returns a list of BIOCs.
+ Args:
+ client (Client): The client to use.
+ args (Dict): The command arguments.
+ Returns:
+ CommandResults: The command results.
+ """
+ filters = create_filters_for_bioc_and_correlation_rules(args)
+ page = arg_to_number(args.get("page")) or 0
+ limit = arg_to_number(args.get("limit")) or 50
+ page_size = arg_to_number(args.get("page_size")) or limit
+ extended_view = argToBoolean(args.get("extra_data", False))
+
+ request_data = assign_params(
+ extended_view=extended_view,
+ search_from=page * page_size,
+ search_to=(page + 1) * page_size,
+ )
+ request_data["filters"] = filters
+ reply = client.get_biocs({"request_data": request_data})
+ biocs = reply.get("objects", [])
+ readable_output = tableToMarkdown(
+ name="BIOCs List",
+ t=biocs,
+ headerTransform=string_to_table_header,
+ headers=["rule_id", "name", "type", "severity", "status"],
+ removeNull=True,
+ )
+ return CommandResults(
+ readable_output=readable_output,
+ outputs_prefix=f"{INTEGRATION_CONTEXT_BRAND}.BIOC",
+ outputs_key_field="rule_id",
+ outputs=biocs,
+ raw_response=reply,
+ )
+
+
+def bioc_create_or_update_helper(client: Client, args: Dict) -> dict:
+ """
+ Creates or updates BIOC indicators based on provided arguments.
+ Args:
+ client (Client): The client to use.
+ args (Dict): The command arguments containing rule configuration.
+ Returns:
+ dict: The raw response from the client containing the update/creation results.
+ """
+ bioc_data = {
+ # required for updating
+ "rule_id": args.get("rule_id"),
+ # required fields
+ "name": args.get("name"),
+ "severity": BIOC_AND_CR_SEVERITY_MAPPING.get(args.get("severity", "")),
+ # not required but need to be null
+ "type": args.get("type"),
+ "is_xql": argToBoolean(args.get("is_xql")) if args.get("is_xql") else None,
+ "comment": args.get("comment"),
+ "status": args.get("status"),
+ "mitre_technique_id_and_name": args.get("mitre_technique_id_and_name") or [],
+ "mitre_tactic_id_and_name": args.get("mitre_tactic_id_and_name") or [],
+ }
+ indicator = args.get("indicator") # required
+ if indicator:
+ try:
+ indicator = json.loads(indicator)
+ bioc_data["indicator"] = indicator
+ except ValueError:
+ raise DemistoException("Unable to parse 'indicator'. Please use the JSON format.")
+
+ return client.insert_or_update_biocs({"request_data": [bioc_data]})
+
+
+def bioc_create_command(client: Client, args: Dict) -> CommandResults:
+ """
+ API Docs https://docs-cortex.paloaltonetworks.com/r/Cortex-XDR-Platform-APIs/Insert-or-update-BIOCs
+ Creates a new BIOC.
+ Args:
+ client (Client): The client to use.
+ args (Dict): The command arguments.
+ Returns:
+ CommandResults: The command results.
+ """
+ reply = bioc_create_or_update_helper(client, args)
+ if added_objects := reply.get("added_objects", []):
+ message = added_objects[0].get("status")
+ outputs = {"rule_id": added_objects[0].get("id")}
+ else:
+ message = "No BIOCs created."
+ outputs = {}
+
+ return CommandResults(
+ readable_output=message,
+ outputs_prefix=f"{INTEGRATION_CONTEXT_BRAND}.BIOC",
+ outputs_key_field="rule_id",
+ outputs=outputs,
+ raw_response=reply,
+ )
+
+
+def bioc_update_command(client: Client, args: Dict) -> CommandResults:
+ """
+ API Docs https://docs-cortex.paloaltonetworks.com/r/Cortex-XDR-Platform-APIs/Insert-or-update-BIOCs
+ Updates an existing BIOC.
+ Args:
+ client (Client): The client to use.
+ args (Dict): The command arguments.
+ Returns:
+ CommandResults: The command results.
+ """
+ reply = bioc_create_or_update_helper(client, args)
+ if updated_objects := reply.get("updated_objects"):
+ message = updated_objects[0].get("status")
+ outputs = {"rule_id": updated_objects[0].get("id")}
+ else:
+ message = "No BIOCs updated."
+ outputs = {}
+
+ return CommandResults(
+ readable_output=message,
+ outputs_prefix=f"{INTEGRATION_CONTEXT_BRAND}.BIOC",
+ outputs_key_field="rule_id",
+ outputs=outputs,
+ raw_response=reply,
+ )
+
+
+def bioc_delete_command(client: Client, args: Dict) -> CommandResults:
+ """
+ API Docs https://docs-cortex.paloaltonetworks.com/r/Cortex-XDR-Platform-APIs/Delete-BIOCs
+ Deletes a BIOC.
+ Args:
+ client (Client): The client to use.
+ args (Dict): The command arguments.
+ Returns:
+ str: Success message.
+ """
+ filters: list = create_filters_for_bioc_and_correlation_rules(args)
+ request_data = {"request_data": {"filters": filters}}
+ res = client.delete_biocs(request_data)
+ rule_ids = res.get("objects", [])
+ count = len(rule_ids)
+
+ if count == 1:
+ return CommandResults(readable_output=f"BIOC with id {rule_ids[0]} deleted successfully.")
+
+ elif count > 1:
+ ids_str = ", ".join(map(str, rule_ids))
+ return CommandResults(readable_output=f"BIOCs with ids {ids_str} deleted successfully.")
+
+ else:
+ return CommandResults(readable_output="No BIOCs were found to delete.")
+
+
+def correlation_rule_list_command(client: Client, args: Dict) -> CommandResults:
+ """
+ Returns a list of correlation rules.
+ Args:
+ client (Client): The client to use.
+ args (Dict): The command arguments.
+ Returns:
+ CommandResults: The command results.
+ """
+ filters = create_filters_for_bioc_and_correlation_rules(args)
+ if filter_json := args.get("filter_json"):
+ try:
+ filter_json = json.loads(filter_json)
+ filters.extend(filter_json)
+ except ValueError:
+ raise DemistoException("Unable to parse 'filter_json'. Please use the JSON format.")
+
+ page = arg_to_number(args.get("page")) or 0
+ limit = arg_to_number(args.get("limit")) or 50
+ page_size = arg_to_number(args.get("page_size")) or limit
+
+ request_data = assign_params(
+ extended_view=argToBoolean(args.get("extra_data", False)),
+ search_from=page * page_size,
+ search_to=(page + 1) * page_size,
+ )
+ request_data["filters"] = filters
+ reply = client.get_correlation_rules({"request_data": request_data})
+ rules = reply.get("objects", [])
+ readable_output = tableToMarkdown(
+ name="Correlation Rules List",
+ t=rules,
+ headers=["rule_id", "name", "severity", "description", "is_enabled"],
+ removeNull=True,
+ headerTransform=string_to_table_header,
+ )
+ return CommandResults(
+ readable_output=readable_output,
+ outputs_prefix=f"{INTEGRATION_CONTEXT_BRAND}.CorrelationRule",
+ outputs_key_field="rule_id",
+ outputs=rules,
+ raw_response=reply,
+ )
+
+
+def correlation_rule_create_or_update_helper(client: Client, args: Dict) -> dict:
+ """
+ Creates or updates correlation rules based on provided arguments.
+ Args:
+ client (Client): The client to use.
+ args (Dict): The command arguments containing rule configuration.
+ Returns:
+ dict: The raw response from the client containing the update/creation results.
+ """
+ severity_arg = args.get("severity")
+ severity_mapped = BIOC_AND_CR_SEVERITY_MAPPING.get(severity_arg) if severity_arg else None
+
+ rule_data = {
+ # Required fields
+ "rule_id": args.get("rule_id"),
+ "name": args.get("name"),
+ "severity": severity_mapped,
+ "xql_query": args.get("xql_query"),
+ "is_enabled": argToBoolean(args.get("is_enabled")) if args.get("is_enabled") else None,
+ "action": "ALERTS",
+ "timezone": args.get("timezone"),
+ "dataset": args.get("dataset"),
+ "alert_category": args.get("alert_category", "").upper() if args.get("alert_category") else None,
+ "execution_mode": args.get("execution_mode", "").upper() if args.get("execution_mode") else None,
+ "mapping_strategy": args.get("mapping_strategy", "").upper() if args.get("mapping_strategy") else None,
+ # Use 'or None' to convert empty strings ("") into JSON null. We must put a value for those fields.
+ "description": args.get("description") or None,
+ "alert_name": args.get("alert_name") or None,
+ "alert_description": args.get("alert_description") or None,
+ "search_window": args.get("search_window") or None,
+ "crontab": args.get("schedule_linux") or None,
+ "simple_schedule": args.get("schedule") or None,
+ "drilldown_query_timeframe": args.get("drilldown_query_timeframe") or None,
+ "investigation_query_link": args.get("investigation_query_link") or None,
+ "suppression_enabled": argToBoolean(args.get("suppression_enabled")) if args.get("suppression_enabled") else None,
+ "suppression_duration": args.get("suppression_duration") or None,
+ "suppression_fields": args.get("suppression_fields") or None,
+ # User Defined Severity (Must be None unless Severity is "User Defined")
+ "user_defined_severity": args.get("user_defined_severity") or None,
+ "user_defined_category": args.get("user_defined_category") or None,
+ # Defaults to "{}" string if missing so json.loads doesn't fail
+ "mitre_defs": json.loads(args.get("mitre_defs_json") or "{}"),
+ "alert_fields": json.loads(args.get("alert_fields") or "{}"),
+ }
+
+ return client.create_or_update_correlation_rules({"request_data": [rule_data]})
+
+
+def correlation_rule_create_command(client: Client, args: Dict) -> CommandResults:
+ """
+ API Docs https://docs-cortex.paloaltonetworks.com/r/Cortex-XDR-Platform-APIs/Insert-or-update-Correlation-Rules
+ Creates a new correlation rule.
+ Args:
+ client (Client): The client to use.
+ args (Dict): The command arguments.
+ Returns:
+ CommandResults: The command results.
+ """
+ reply = correlation_rule_create_or_update_helper(client, args)
+ if added_objects := reply.get("added_objects", []):
+ message = added_objects[0].get("status")
+ outputs = {"rule_id": added_objects[0].get("id")}
+ else:
+ message = "No Correlation Rules created."
+ outputs = {}
+ return CommandResults(
+ readable_output=message,
+ outputs_prefix=f"{INTEGRATION_CONTEXT_BRAND}.CorrelationRule",
+ outputs_key_field="rule_id",
+ outputs=outputs,
+ raw_response=reply,
+ )
+
+
+def correlation_rule_update_command(client: Client, args: Dict) -> CommandResults:
+ """
+ API Docs https://docs-cortex.paloaltonetworks.com/r/Cortex-XDR-Platform-APIs/Insert-or-update-Correlation-Rules
+ Updates an existing correlation rule.
+ Args:
+ client (Client): The client to use.
+ args (Dict): The command arguments.
+ Returns:
+ CommandResults: The command results.
+ """
+ reply = correlation_rule_create_or_update_helper(client, args)
+ if updated_objects := reply.get("updated_objects"):
+ message = updated_objects[0].get("status")
+ outputs = {"rule_id": updated_objects[0].get("id")}
+ else:
+ message = "No Correlation Rules updated."
+ outputs = {}
+ return CommandResults(
+ readable_output=message,
+ outputs_prefix=f"{INTEGRATION_CONTEXT_BRAND}.CorrelationRule",
+ outputs_key_field="rule_id",
+ outputs=outputs,
+ raw_response=reply,
+ )
+
+
+def correlation_rule_delete_command(client: Client, args: Dict) -> CommandResults:
+ """
+ API Docs https://docs-cortex.paloaltonetworks.com/r/Cortex-XDR-Platform-APIs/Delete-Correlation-Rules
+ Deletes correlation rules.
+ Args:
+ client (Client): The client to use.
+ args (Dict): The command arguments.
+ Returns:
+ str: Success message.
+ """
+ rule_ids = argToList(args.get("rule_id"))
+ rule_id_list = []
+ for rule_id in rule_ids:
+ try:
+ rule_id_list.append({"field": "rule_id", "operator": "EQ", "value": int(rule_id)})
+ except (ValueError, TypeError):
+ # If rule_id is None, "abc", or "", skip it safely
+ continue
+
+ reply = client.delete_correlation_rules({"request_data": {"filters": rule_id_list}})
+ deleted_ids = reply.get("objects", [])
+ objects_count = reply.get("objects_count")
+
+ if objects_count == 0:
+ status = "Could not find any correlation rules to delete."
+ elif objects_count == 1:
+ status = f"Correlation Rule {deleted_ids[0]} was deleted."
+ else:
+ ids_str = ", ".join(map(str, deleted_ids))
+ status = f"Correlation Rules {ids_str} were deleted."
+
+ return CommandResults(readable_output=status, raw_response=reply)
+
+
def api_key_list_command(client: Client, args: Dict[str, Any]) -> CommandResults:
"""
API Docs: https://docs-cortex.paloaltonetworks.com/r/Cortex-XDR-Platform-APIs/Get-existing-API-keys
@@ -2369,6 +2841,30 @@ def main(): # pragma: no cover
elif command == "xdr-update-alert":
return_results(update_alerts_in_xdr_command(client, args))
+ elif command == "xdr-bioc-list":
+ return_results(bioc_list_command(client, args))
+
+ elif command == "xdr-bioc-create":
+ return_results(bioc_create_command(client, args))
+
+ elif command == "xdr-bioc-update":
+ return_results(bioc_update_command(client, args))
+
+ elif command == "xdr-bioc-delete":
+ return_results(bioc_delete_command(client, args))
+
+ elif command == "xdr-correlation-rule-list":
+ return_results(correlation_rule_list_command(client, args))
+
+ elif command == "xdr-correlation-rule-create":
+ return_results(correlation_rule_create_command(client, args))
+
+ elif command == "xdr-correlation-rule-update":
+ return_results(correlation_rule_update_command(client, args))
+
+ elif command == "xdr-correlation-rule-delete":
+ return_results(correlation_rule_delete_command(client, args))
+
elif command == "xdr-api-key-list":
return_results(api_key_list_command(client, args))
diff --git a/Packs/CortexXDR/Integrations/CortexXDRIR/CortexXDRIR.yml b/Packs/CortexXDR/Integrations/CortexXDRIR/CortexXDRIR.yml
index a3d31dddb5df..48fc8cbe56ea 100644
--- a/Packs/CortexXDR/Integrations/CortexXDRIR/CortexXDRIR.yml
+++ b/Packs/CortexXDR/Integrations/CortexXDRIR/CortexXDRIR.yml
@@ -4132,6 +4132,565 @@ script:
name: membership_predicate_json
description: Updates an asset group.
name: xdr-asset-group-update
+ - arguments:
+ - description: 'The BIOC name to filter by. Can filter only by one name.'
+ name: name
+ - auto: PREDEFINED
+ description: 'The BIOC severity to filter by.'
+ name: severity
+ predefined:
+ - info
+ - low
+ - medium
+ - high
+ - critical
+ - auto: PREDEFINED
+ description: 'The BIOC type to filter by.'
+ name: type
+ predefined:
+ - other
+ - persistence
+ - evasion
+ - tampering
+ - file_type_obfuscation
+ - privilege_escalation
+ - credential_access
+ - lateral_movement
+ - execution
+ - collection
+ - exfiltration
+ - infiltration
+ - dropper
+ - file_privilege_manipulation
+ - reconnaissance
+ - discovery
+ - auto: PREDEFINED
+ description: 'Whether the BIOC is XQL.'
+ name: is_xql
+ predefined:
+ - 'true'
+ - 'false'
+ - description: 'The BIOC comment to filter by.'
+ name: comment
+ - auto: PREDEFINED
+ description: 'The BIOC status to filter by. Can be one of the following: enabled, disabled.'
+ name: status
+ predefined:
+ - enabled
+ - disabled
+ - description: 'The BIOC indicator to filter by.'
+ name: indicator
+ - description: "The MITRE technique ID and name. Must be in format 'ID - Name', for example: ['T1566 - Phishing']."
+ isArray: true
+ name: mitre_technique_id_and_name
+ - description: "The MITRE tactic ID and name. Must be in format 'ID - Name', for example: ['T1566 - Phishing']."
+ isArray: true
+ name: mitre_tactic_id_and_name
+ - auto: PREDEFINED
+ description: Whether to return extended data.
+ name: extra_data
+ predefined:
+ - 'true'
+ - 'false'
+ - description: Maximum number of results to return.
+ name: limit
+ - description: Page size.
+ name: page_size
+ - description: Page number.
+ name: page
+ description: Returns a list of BIOCs.
+ name: xdr-bioc-list
+ outputs:
+ - contextPath: PaloAltoNetworksXDR.BIOC.rule_id
+ description: BIOC rule id.
+ type: String
+ - contextPath: PaloAltoNetworksXDR.BIOC.name
+ description: BIOC name.
+ type: String
+ - contextPath: PaloAltoNetworksXDR.BIOC.type
+ description: BIOC type.
+ type: String
+ - contextPath: PaloAltoNetworksXDR.BIOC.severity
+ description: BIOC severity.
+ type: String
+ - contextPath: PaloAltoNetworksXDR.BIOC.status
+ description: BIOC status.
+ type: String
+ - contextPath: PaloAltoNetworksXDR.BIOC.is_xql
+ description: Whether the BIOC is XQL.
+ type: Boolean
+ - contextPath: PaloAltoNetworksXDR.BIOC.comment
+ description: The BIOC comment.
+ type: String
+ - contextPath: PaloAltoNetworksXDR.BIOC.indicator
+ description: The BIOC indicator.
+ type: String
+ - arguments:
+ - description: The BIOC name.
+ name: name
+ required: true
+ - auto: PREDEFINED
+ description: The BIOC severity.
+ name: severity
+ required: true
+ predefined:
+ - info
+ - low
+ - medium
+ - high
+ - critical
+ - auto: PREDEFINED
+ description: The BIOC type.
+ name: type
+ predefined:
+ - other
+ - persistence
+ - evasion
+ - tampering
+ - file_type_obfuscation
+ - privilege_escalation
+ - credential_access
+ - lateral_movement
+ - execution
+ - collection
+ - exfiltration
+ - infiltration
+ - dropper
+ - file_privilege_manipulation
+ - reconnaissance
+ - discovery
+ - auto: PREDEFINED
+ description: Whether the new BIOC is XQL.
+ name: is_xql
+ predefined:
+ - 'true'
+ - 'false'
+ - description: The BIOC comment.
+ name: comment
+ - auto: PREDEFINED
+ description: The BIOC status.
+ name: status
+ predefined:
+ - enabled
+ - disabled
+ - description: |-
+ The BIOC indicator,
+ for example: '{\"runOnCGO\":true,\"investigationType\":\"FILE_EVENT\",\"investigation\":{\"FILE_EVENT\":{\"filter\":{\"AND\":[{\"SEARCH_FIELD\":\"action_file_name\",\"SEARCH_TYPE\":\"EQ\",\"SEARCH_VALUE\":\"testfile.exe\"}]}}}}'.
+ For more information please refer to the documentation https://docs-cortex.paloaltonetworks.com/r/Cortex-XDR-Platform-APIs/Insert-or-update-BIOCs.
+ isArray: true
+ name: indicator
+ required: true
+ - description: "The MITRE technique ID and name. Must be in format 'ID - Name', for example: ['T1566 - Phishing']."
+ isArray: true
+ name: mitre_technique_id_and_name
+ - description: "The MITRE tactic ID and name. Must be in format 'ID - Name', for example: ['TA0001 - Initial Access']."
+ isArray: true
+ name: mitre_tactic_id_and_name
+ description: Creates a new BIOC.
+ name: xdr-bioc-create
+ outputs:
+ - contextPath: PaloAltoNetworksXDR.BIOC.rule_id
+ description: BIOC ID.
+ type: String
+ - arguments:
+ - description: BIOC rule ID.
+ name: rule_id
+ required: true
+ - description: BIOC name.
+ name: name
+ required: true
+ - auto: PREDEFINED
+ description: BIOC severity.
+ name: severity
+ required: true
+ predefined:
+ - info
+ - low
+ - medium
+ - high
+ - critical
+ - auto: PREDEFINED
+ description: BIOC type.
+ name: type
+ predefined:
+ - other
+ - persistence
+ - evasion
+ - tampering
+ - file_type_obfuscation
+ - privilege_escalation
+ - credential_access
+ - lateral_movement
+ - execution
+ - collection
+ - exfiltration
+ - infiltration
+ - dropper
+ - file_privilege_manipulation
+ - reconnaissance
+ - discovery
+ - auto: PREDEFINED
+ description: Whether the BIOC is XQL.
+ name: is_xql
+ predefined:
+ - 'true'
+ - 'false'
+ - description: BIOC comment.
+ name: comment
+ - auto: PREDEFINED
+ description: BIOC status.
+ name: status
+ predefined:
+ - enabled
+ - disabled
+ - description: |-
+ The BIOC indicator,
+ for example: '{\"runOnCGO\":true,\"investigationType\":\"FILE_EVENT\",\"investigation\":{\"FILE_EVENT\":{\"filter\":{\"AND\":[{\"SEARCH_FIELD\":\"action_file_name\",\"SEARCH_TYPE\":\"EQ\",\"SEARCH_VALUE\":\"testfile.exe\"}]}}}}'.
+ For more information please refer to the documentation https://docs-cortex.paloaltonetworks.com/r/Cortex-XDR-Platform-APIs/Insert-or-update-BIOCs.
+ isArray: true
+ name: indicator
+ required: true
+ - description: "The MITRE technique ID and name. Must be in format 'ID - Name', for example: ['T1566 - Phishing']."
+ isArray: true
+ name: mitre_technique_id_and_name
+ - description: "The MITRE tactic ID and name. Must be in format 'ID - Name', for example: ['TA0001 - Initial Access']."
+ isArray: true
+ name: mitre_tactic_id_and_name
+ description: Updates an existing BIOC.
+ name: xdr-bioc-update
+ outputs:
+ - contextPath: PaloAltoNetworksXDR.BIOC.rule_id
+ description: BIOC ID.
+ type: String
+ - arguments:
+ - description: BIOC name.
+ name: name
+ - auto: PREDEFINED
+ description: BIOC severity.
+ name: severity
+ predefined:
+ - info
+ - low
+ - medium
+ - high
+ - critical
+ - auto: PREDEFINED
+ description: BIOC type.
+ name: type
+ predefined:
+ - other
+ - persistence
+ - evasion
+ - tampering
+ - file_type_obfuscation
+ - privilege_escalation
+ - credential_access
+ - lateral_movement
+ - execution
+ - collection
+ - exfiltration
+ - infiltration
+ - dropper
+ - file_privilege_manipulation
+ - reconnaissance
+ - discovery
+ - auto: PREDEFINED
+ description: Whether the BIOC is XQL.
+ name: is_xql
+ predefined:
+ - 'true'
+ - 'false'
+ - description: BIOC comment.
+ name: comment
+ - description: BIOC indicator.
+ isArray: true
+ name: indicator
+ - description: "The MITRE technique ID and name. Must be in format 'ID - Name', for example: ['T1566 - Phishing']."
+ isArray: true
+ name: mitre_technique_id_and_name
+ - description: "The MITRE tactic ID and name. Must be in format 'ID - Name', for example: ['TA0001 - Initial Access']."
+ isArray: true
+ name: mitre_tactic_id_and_name
+ description: Deletes a BIOC.
+ name: xdr-bioc-delete
+ - arguments:
+ - description: Correlation rule name.
+ name: name
+ - auto: PREDEFINED
+ description: Correlation rule severity.
+ name: severity
+ predefined:
+ - info
+ - low
+ - medium
+ - high
+ - critical
+ - description: Correlation rule XQL query.
+ name: xql_query
+ - auto: PREDEFINED
+ description: Whether the correlation rule is XQL.
+ name: is_xql
+ predefined:
+ - 'true'
+ - 'false'
+ - description: Correlation rule dataset.
+ name: dataset
+ - description: Alert name.
+ name: alert_name
+ - description: Alert category.
+ name: alert_category
+ - description: Alert fields. Can be a string or a dictionary.
+ name: alert_fields
+ - description: Alert domain.
+ name: alert_domain
+ - description: Filter JSON.
+ name: filter_json
+ - auto: PREDEFINED
+ description: Whether to return extended view.
+ name: extra_data
+ predefined:
+ - 'true'
+ - 'false'
+ - description: Maximum number of results to return.
+ name: limit
+ - description: Page size.
+ name: page_size
+ - description: Page number.
+ name: page
+ description: Returns a list of correlation rules.
+ name: xdr-correlation-rule-list
+ outputs:
+ - contextPath: PaloAltoNetworksXDR.CorrelationRule.rule_id
+ description: Correlation rule ID.
+ type: String
+ - contextPath: PaloAltoNetworksXDR.CorrelationRule.name
+ description: Correlation rule name.
+ type: String
+ - contextPath: PaloAltoNetworksXDR.CorrelationRule.description
+ description: Correlation rule description.
+ type: String
+ - contextPath: PaloAltoNetworksXDR.CorrelationRule.is_enabled
+ description: Whether the correlation rule is enabled.
+ type: Boolean
+ - arguments:
+ - description: 'The correlation rule name. Example: name="This is a CR test 6"'
+ name: name
+ required: true
+ - auto: PREDEFINED
+ description: 'The correlation rule severity. Example: severity=low'
+ name: severity
+ required: true
+ predefined:
+ - info
+ - low
+ - medium
+ - high
+ - critical
+ - description: 'The correlation rule XQL query. Example: xql_query="dataset = xdr_data | limit 1"'
+ name: xql_query
+ required: true
+ - auto: PREDEFINED
+ description: 'Whether the rule is enabled. Example: is_enabled=true'
+ name: is_enabled
+ required: true
+ predefined:
+ - 'true'
+ - 'false'
+ - auto: PREDEFINED
+ description: 'The alert category. Example: alert_category=dropper'
+ name: alert_category
+ required: true
+ predefined:
+ - other
+ - persistence
+ - evasion
+ - tampering
+ - file_type_obfuscation
+ - privilege_escalation
+ - credential_access
+ - lateral_movement
+ - execution
+ - collection
+ - exfiltration
+ - infiltration
+ - dropper
+ - file_privilege_manipulation
+ - reconnaissance
+ - discovery
+ - auto: PREDEFINED
+ description: 'The rule execution mode. Example: execution_mode=scheduled'
+ name: execution_mode
+ required: true
+ predefined:
+ - scheduled
+ - real_time
+ - description: 'The correlation rule timezone. Example: timezone="Asia/Jerusalem"'
+ name: timezone
+ required: true
+ - auto: PREDEFINED
+ description: 'The rule mapping strategy. Example: mapping_strategy=auto'
+ name: mapping_strategy
+ required: true
+ predefined:
+ - auto
+ - custom
+ - description: The correlation rule description.
+ name: description
+ - description: The alert name.
+ name: alert_name
+ - description: The alert description.
+ name: alert_description
+ - description: Alert fields (string or dictionary).
+ name: alert_fields
+ - description: 'The search window timeframe. Example: search_window="1 hours"'
+ name: search_window
+ - description: 'The correlation rule schedule. Example: schedule="10 minutes"'
+ name: schedule
+ - description: 'Linux scheduling for the rule. Example: schedule_linux="*/10 * * * *"'
+ name: schedule_linux
+ - auto: PREDEFINED
+ description: Whether suppression is enabled.
+ name: suppression_enabled
+ predefined:
+ - 'true'
+ - 'false'
+ - description: Duration of correlation rule suppression.
+ name: suppression_duration
+ - description: Suppression fields.
+ name: suppression_fields
+ - description: 'The correlation rule dataset. Example: dataset=alerts'
+ name: dataset
+ required: true
+ - description: User-defined severity.
+ name: user_defined_severity
+ - description: User-defined category.
+ name: user_defined_category
+ - description: MITRE definitions.
+ name: mitre_defs_json
+ - description: Investigation query link.
+ name: investigation_query_link
+ - description: 'The drilldown query timeframe. Example: drilldown_query_timeframe="ALERT"'
+ name: drilldown_query_timeframe
+ description: Creates a new correlation rule.
+ name: xdr-correlation-rule-create
+ outputs:
+ - contextPath: PaloAltoNetworksXDR.CorrelationRule.rule_id
+ description: Correlation rule ID.
+ type: String
+ - arguments:
+ - description: Correlation rule ID.
+ name: rule_id
+ required: true
+ - description: 'The correlation rule name. Example: name="This is a CR test 6"'
+ name: name
+ required: true
+ - auto: PREDEFINED
+ description: 'The correlation rule severity. Example: severity=low'
+ name: severity
+ required: true
+ predefined:
+ - info
+ - low
+ - medium
+ - high
+ - critical
+ - description: 'The correlation rule XQL query. Example: xql_query="dataset = xdr_data | limit 1"'
+ name: xql_query
+ required: true
+ - auto: PREDEFINED
+ description: 'Whether the rule is enabled. Example: is_enabled=true'
+ name: is_enabled
+ required: true
+ predefined:
+ - 'true'
+ - 'false'
+ - auto: PREDEFINED
+ description: 'The alert category. Example: alert_category=dropper'
+ name: alert_category
+ required: true
+ predefined:
+ - other
+ - persistence
+ - evasion
+ - tampering
+ - file_type_obfuscation
+ - privilege_escalation
+ - credential_access
+ - lateral_movement
+ - execution
+ - collection
+ - exfiltration
+ - infiltration
+ - dropper
+ - file_privilege_manipulation
+ - reconnaissance
+ - discovery
+ - description: 'The rule execution mode. Example: execution_mode=scheduled'
+ name: execution_mode
+ required: true
+ predefined:
+ - scheduled
+ - real_time
+ - description: 'The correlation rule timezone. Example: timezone="Asia/Jerusalem"'
+ name: timezone
+ required: true
+ - auto: PREDEFINED
+ description: 'The rule mapping strategy. Example: mapping_strategy=auto'
+ name: mapping_strategy
+ required: true
+ predefined:
+ - auto
+ - custom
+ - description: The correlation rule description.
+ name: description
+ - description: The alert name.
+ name: alert_name
+ - description: The alert description.
+ name: alert_description
+ - description: Alert fields (string or dictionary).
+ name: alert_fields
+ - description: 'The search window timeframe. Example: search_window="1 hours"'
+ name: search_window
+ - description: 'The correlation rule schedule. Example: schedule="10 minutes"'
+ name: schedule
+ - description: 'Linux scheduling for the rule. Example: schedule_linux="*/10 * * * *"'
+ name: schedule_linux
+ - auto: PREDEFINED
+ description: Whether suppression is enabled.
+ name: suppression_enabled
+ predefined:
+ - 'true'
+ - 'false'
+ - description: Duration of correlation rule suppression.
+ name: suppression_duration
+ - description: Suppression fields.
+ name: suppression_fields
+ - description: 'The correlation rule dataset. Example: dataset=alerts'
+ name: dataset
+ required: true
+ - description: User-defined severity.
+ name: user_defined_severity
+ - description: User-defined category.
+ name: user_defined_category
+ - description: MITRE definitions.
+ name: mitre_defs_json
+ - description: Investigation query link.
+ name: investigation_query_link
+ - description: 'The drilldown query timeframe. Example: drilldown_query_timeframe="ALERT"'
+ name: drilldown_query_timeframe
+ description: Updates an existing correlation rule.
+ name: xdr-correlation-rule-update
+ outputs:
+ - contextPath: PaloAltoNetworksXDR.CorrelationRule.id
+ description: Correlation rule ID.
+ type: String
+ - arguments:
+ - description: Correlation rule ID.
+ isArray: true
+ name: rule_id
+ required: true
+ description: Deletes correlation rules.
+ name: xdr-correlation-rule-delete
dockerimage: demisto/python3:3.12.12.5490952
isfetch: true
isfetch:xpanse: false
diff --git a/Packs/CortexXDR/Integrations/CortexXDRIR/CortexXDRIR_test.py b/Packs/CortexXDR/Integrations/CortexXDRIR/CortexXDRIR_test.py
index c019b9c6ef0d..64aefe45bab6 100644
--- a/Packs/CortexXDR/Integrations/CortexXDRIR/CortexXDRIR_test.py
+++ b/Packs/CortexXDR/Integrations/CortexXDRIR/CortexXDRIR_test.py
@@ -2785,3 +2785,523 @@ def test_replace_dots_in_keys():
# Test with non-dict/list input
assert replace_dots_in_keys("string.with.dots") == "string.with.dots"
assert replace_dots_in_keys(123) == 123
+
+
+@pytest.mark.parametrize(
+ "args, expected_request_data",
+ [
+ (
+ {"name": "test_bioc", "severity": "high", "page": "1", "limit": "10", "extra_data": "true"},
+ {
+ "request_data": {
+ "filters": [
+ {"field": "name", "operator": "EQ", "value": "test_bioc"},
+ {"field": "severity", "operator": "EQ", "value": "SEV_040_HIGH"},
+ ],
+ "extended_view": True,
+ "search_from": 10,
+ "search_to": 20,
+ }
+ },
+ ),
+ (
+ {},
+ {
+ "request_data": {
+ "extended_view": False,
+ "search_from": 0,
+ "search_to": 50,
+ "filters": [],
+ }
+ },
+ ),
+ ],
+)
+def test_bioc_list_command(mocker, args, expected_request_data):
+ """
+ Given:
+ - Arguments for filtering BIOCs.
+ When:
+ - Running bioc_list_command.
+ Then:
+ - Verify the client.get_biocs is called with correct parameters.
+ """
+ from CortexXDRIR import Client, bioc_list_command
+
+ client = Client(base_url=f"{XDR_URL}/public_api/v1", verify=False, timeout=120, proxy=False)
+ mock_reply = {"objects": [{"rule_id": "1", "name": "test_bioc", "type": "host", "severity": "high", "status": "enabled"}]}
+ mocker.patch.object(Client, "get_biocs", return_value=mock_reply)
+
+ res = bioc_list_command(client, args)
+
+ assert res.outputs == mock_reply["objects"]
+ Client.get_biocs.assert_called_with(expected_request_data)
+
+
+@pytest.mark.parametrize(
+ "args, expected_request_data, expected_output, expected_error",
+ [
+ (
+ {
+ "name": "test_bioc",
+ "severity": "high",
+ "type": "bioc_type",
+ "is_xql": "true",
+ "comment": "test_comment",
+ "status": "enabled",
+ "mitre_technique_id_and_name": "T1000",
+ "mitre_tactic_id_and_name": "TA0001",
+ "indicator": '{"field": "value"}',
+ },
+ {
+ "request_data": [
+ {
+ "rule_id": None,
+ "name": "test_bioc",
+ "severity": "SEV_040_HIGH",
+ "type": "bioc_type",
+ "is_xql": True,
+ "comment": "test_comment",
+ "status": "enabled",
+ "mitre_technique_id_and_name": "T1000",
+ "mitre_tactic_id_and_name": "TA0001",
+ "indicator": {"field": "value"},
+ }
+ ]
+ },
+ {"rule_id": "1", "readable_output": "BIOC created successfully"},
+ None,
+ ),
+ (
+ {"name": "test_bioc", "severity": "high", "indicator": "invalid_json"},
+ None,
+ None,
+ "Unable to parse 'indicator'. Please use the JSON format.",
+ ),
+ ],
+)
+def test_bioc_create_command(mocker, args, expected_request_data, expected_output, expected_error):
+ """
+ Given:
+ - Arguments for creating a BIOC.
+ When:
+ - Running bioc_create_command.
+ Then:
+ - Verify the client.insert_or_update_biocs is called and results are correct.
+ """
+ from CortexXDRIR import Client, bioc_create_command
+
+ client = Client(base_url=f"{XDR_URL}/public_api/v1", verify=False, timeout=120, proxy=False)
+ mock_reply = {"added_objects": [{"id": "1", "status": "BIOC created successfully"}]}
+ mocker.patch.object(Client, "insert_or_update_biocs", return_value=mock_reply)
+
+ if expected_error:
+ with pytest.raises(DemistoException) as e:
+ bioc_create_command(client, args)
+ assert expected_error in str(e.value)
+ else:
+ res = bioc_create_command(client, args)
+ assert res.outputs == {"rule_id": expected_output["rule_id"]}
+ assert res.readable_output == expected_output["readable_output"]
+ Client.insert_or_update_biocs.assert_called_with(expected_request_data)
+
+
+@pytest.mark.parametrize(
+ "mock_reply, expected_output, expected_readable_output",
+ [
+ (
+ {"updated_objects": [{"id": "1", "status": "BIOC updated successfully"}]},
+ {"rule_id": "1"},
+ "BIOC updated successfully",
+ ),
+ (
+ {"updated_objects": []},
+ {},
+ "No BIOCs updated.",
+ ),
+ ],
+)
+def test_bioc_update_command(mocker, mock_reply, expected_output, expected_readable_output):
+ """
+ Given:
+ - Arguments for updating a BIOC.
+ When:
+ - Running bioc_update_command.
+ Then:
+ - Verify the client.insert_or_update_biocs is called and results are correct.
+ """
+ from CortexXDRIR import Client, bioc_update_command
+
+ client = Client(base_url=f"{XDR_URL}/public_api/v1", verify=False, timeout=120, proxy=False)
+ mocker.patch.object(Client, "insert_or_update_biocs", return_value=mock_reply)
+
+ args = {"rule_id": "1", "name": "test_bioc", "severity": "high"}
+ res = bioc_update_command(client, args)
+
+ assert res.outputs == expected_output
+ assert res.readable_output == expected_readable_output
+
+
+@pytest.mark.parametrize(
+ "mock_reply, expected_output",
+ [
+ ({"objects": ["1"]}, "BIOC with id 1 deleted successfully."),
+ ({"objects": ["1", "2"]}, "BIOCs with ids 1, 2 deleted successfully."),
+ ({"objects": []}, "No BIOCs were found to delete."),
+ ],
+)
+def test_bioc_delete_command(mocker, mock_reply, expected_output):
+ """
+ Given:
+ - Arguments for deleting a BIOC.
+ When:
+ - Running bioc_delete_command.
+ Then:
+ - Verify the client.delete_biocs is called and results are correct.
+ """
+ from CortexXDRIR import Client, bioc_delete_command
+
+ client = Client(base_url=f"{XDR_URL}/public_api/v1", verify=False, timeout=120, proxy=False)
+ mocker.patch.object(Client, "delete_biocs", return_value=mock_reply)
+
+ args = {"name": "test_bioc"}
+ res = bioc_delete_command(client, args)
+
+ assert res.readable_output == expected_output
+
+
+@pytest.mark.parametrize(
+ "args, expected_request_data",
+ [
+ (
+ {"name": "test_rule", "page": "1", "limit": "10", "extra_data": "true"},
+ {
+ "request_data": {
+ "filters": [{"field": "name", "operator": "EQ", "value": "test_rule"}],
+ "extended_view": True,
+ "search_from": 10,
+ "search_to": 20,
+ }
+ },
+ ),
+ (
+ {"filter_json": '[{"field": "name", "operator": "EQ", "value": "test_rule"}]'},
+ {
+ "request_data": {
+ "filters": [{"field": "name", "operator": "EQ", "value": "test_rule"}],
+ "extended_view": False,
+ "search_from": 0,
+ "search_to": 50,
+ }
+ },
+ ),
+ ],
+)
+def test_correlation_rule_list_command(mocker, args, expected_request_data):
+ """
+ Given:
+ - Arguments for filtering correlation rules.
+ When:
+ - Running correlation_rule_list_command.
+ Then:
+ - Verify the client.get_correlation_rules is called and results are correct.
+ """
+ from CortexXDRIR import Client, correlation_rule_list_command
+
+ client = Client(base_url=f"{XDR_URL}/public_api/v1", verify=False, timeout=120, proxy=False)
+ mock_reply = {"objects": [{"rule_id": "1", "name": "test_rule", "description": "desc", "is_enabled": True}]}
+ mocker.patch.object(Client, "get_correlation_rules", return_value=mock_reply)
+
+ res = correlation_rule_list_command(client, args)
+
+ assert res.outputs == mock_reply["objects"]
+ Client.get_correlation_rules.assert_called_with(expected_request_data)
+
+
+@pytest.mark.parametrize(
+ "args, expected_request_data, expected_output",
+ [
+ (
+ {
+ "name": "test_rule",
+ "severity": "high",
+ "xql_query": "dataset = xdr_data",
+ "is_enabled": "true",
+ "timezone": "UTC",
+ "dataset": "xdr_data",
+ "alert_category": "category",
+ "execution_mode": "real_time",
+ "mapping_strategy": "strategy",
+ "description": "desc",
+ "alert_name": "alert",
+ "alert_description": "alert_desc",
+ "search_window": "1h",
+ "schedule_linux": "* * * * *",
+ "schedule": "daily",
+ "drilldown_query_timeframe": "1h",
+ "investigation_query_link": "link",
+ "suppression_enabled": "true",
+ "suppression_duration": "1h",
+ "suppression_fields": "field1",
+ "user_defined_severity": "medium",
+ "user_defined_category": "cat",
+ "mitre_defs_json": '{"tactic": "test"}',
+ "alert_fields": '{"field": "value"}',
+ },
+ {
+ "request_data": [
+ {
+ "rule_id": None,
+ "name": "test_rule",
+ "severity": "SEV_040_HIGH",
+ "xql_query": "dataset = xdr_data",
+ "is_enabled": True,
+ "action": "ALERTS",
+ "timezone": "UTC",
+ "dataset": "xdr_data",
+ "alert_category": "CATEGORY",
+ "execution_mode": "REAL_TIME",
+ "mapping_strategy": "STRATEGY",
+ "description": "desc",
+ "alert_name": "alert",
+ "alert_description": "alert_desc",
+ "search_window": "1h",
+ "crontab": "* * * * *",
+ "simple_schedule": "daily",
+ "drilldown_query_timeframe": "1h",
+ "investigation_query_link": "link",
+ "suppression_enabled": True,
+ "suppression_duration": "1h",
+ "suppression_fields": "field1",
+ "user_defined_severity": "medium",
+ "user_defined_category": "cat",
+ "mitre_defs": {"tactic": "test"},
+ "alert_fields": {"field": "value"},
+ }
+ ]
+ },
+ {"rule_id": "1", "readable_output": "Rule created successfully"},
+ )
+ ],
+)
+def test_correlation_rule_create_command(mocker, args, expected_request_data, expected_output):
+ """
+ Given:
+ - Arguments for creating a correlation rule.
+ When:
+ - Running correlation_rule_create_command.
+ Then:
+ - Verify the client.create_or_update_correlation_rules is called and results are correct.
+ """
+ from CortexXDRIR import Client, correlation_rule_create_command
+
+ client = Client(base_url=f"{XDR_URL}/public_api/v1", verify=False, timeout=120, proxy=False)
+ mock_reply = {"added_objects": [{"id": "1", "status": "Rule created successfully"}]}
+ mocker.patch.object(Client, "create_or_update_correlation_rules", return_value=mock_reply)
+
+ res = correlation_rule_create_command(client, args)
+
+ assert res.outputs == {"rule_id": expected_output["rule_id"]}
+ assert res.readable_output == expected_output["readable_output"]
+ Client.create_or_update_correlation_rules.assert_called_with(expected_request_data)
+
+
+@pytest.mark.parametrize(
+ "args, expected_request_data, expected_output",
+ [
+ (
+ {"rule_id": "1", "name": "test_rule", "severity": "high", "is_enabled": "true"},
+ {
+ "request_data": [
+ {
+ "rule_id": "1",
+ "name": "test_rule",
+ "severity": "SEV_040_HIGH",
+ "xql_query": None,
+ "is_enabled": True,
+ "action": "ALERTS",
+ "timezone": None,
+ "dataset": None,
+ "alert_category": None,
+ "execution_mode": None,
+ "mapping_strategy": None,
+ "description": None,
+ "alert_name": None,
+ "alert_description": None,
+ "search_window": None,
+ "crontab": None,
+ "simple_schedule": None,
+ "drilldown_query_timeframe": None,
+ "investigation_query_link": None,
+ "suppression_enabled": None,
+ "suppression_duration": None,
+ "suppression_fields": None,
+ "user_defined_severity": None,
+ "user_defined_category": None,
+ "mitre_defs": {},
+ "alert_fields": {},
+ }
+ ]
+ },
+ {"rule_id": "1", "readable_output": "Rule updated successfully"},
+ )
+ ],
+)
+def test_correlation_rule_update_command(mocker, args, expected_request_data, expected_output):
+ """
+ Given:
+ - Arguments for updating a correlation rule.
+ When:
+ - Running correlation_rule_update_command.
+ Then:
+ - Verify the client.create_or_update_correlation_rules is called and results are correct.
+ """
+ from CortexXDRIR import Client, correlation_rule_update_command
+
+ client = Client(base_url=f"{XDR_URL}/public_api/v1", verify=False, timeout=120, proxy=False)
+ mock_reply = {"updated_objects": [{"id": "1", "status": "Rule updated successfully"}]}
+ mocker.patch.object(Client, "create_or_update_correlation_rules", return_value=mock_reply)
+
+ res = correlation_rule_update_command(client, args)
+
+ assert res.outputs == {"rule_id": expected_output["rule_id"]}
+ assert res.readable_output == expected_output["readable_output"]
+ Client.create_or_update_correlation_rules.assert_called_with(expected_request_data)
+
+
+@pytest.mark.parametrize(
+ "mock_reply, args, expected_output, expected_filters",
+ [
+ (
+ {"objects": ["1"], "objects_count": 1},
+ {"rule_id": "1"},
+ "Correlation Rule 1 was deleted.",
+ {"request_data": {"filters": [{"field": "rule_id", "operator": "EQ", "value": 1}]}},
+ ),
+ (
+ {"objects": ["1", "2"], "objects_count": 2},
+ {"rule_id": "1,2"},
+ "Correlation Rules 1, 2 were deleted.",
+ {
+ "request_data": {
+ "filters": [
+ {"field": "rule_id", "operator": "EQ", "value": 1},
+ {"field": "rule_id", "operator": "EQ", "value": 2},
+ ]
+ }
+ },
+ ),
+ (
+ {"objects": [], "objects_count": 0},
+ {"rule_id": "1"},
+ "Could not find any correlation rules to delete.",
+ {"request_data": {"filters": [{"field": "rule_id", "operator": "EQ", "value": 1}]}},
+ ),
+ (
+ {"objects": ["1"], "objects_count": 1},
+ {"rule_id": "1,invalid"},
+ "Correlation Rule 1 was deleted.",
+ {"request_data": {"filters": [{"field": "rule_id", "operator": "EQ", "value": 1}]}},
+ ),
+ ],
+)
+def test_correlation_rule_delete_command(mocker, mock_reply, args, expected_output, expected_filters):
+ """
+ Given:
+ - Arguments for deleting correlation rules.
+ When:
+ - Running correlation_rule_delete_command.
+ Then:
+ - Verify the client.delete_correlation_rules is called and results are correct.
+ """
+ from CortexXDRIR import Client, correlation_rule_delete_command
+
+ client = Client(base_url=f"{XDR_URL}/public_api/v1", verify=False, timeout=120, proxy=False)
+ mocker.patch.object(Client, "delete_correlation_rules", return_value=mock_reply)
+
+ res = correlation_rule_delete_command(client, args)
+
+ assert res.readable_output == expected_output
+ Client.delete_correlation_rules.assert_called_with(expected_filters)
+
+
+def test_create_filters_for_bioc_and_correlation_rules_all_fields():
+ """
+ Given:
+ - All possible arguments for creating filters.
+ When:
+ - Running create_filters_for_bioc_and_correlation_rules.
+ Then:
+ - Verify the returned filters list contains all expected filters.
+ """
+ from CortexXDRIR import create_filters_for_bioc_and_correlation_rules
+
+ args = {
+ "name": "test_name",
+ "severity": "high",
+ "type": "bioc_type",
+ "is_xql": "true",
+ "comment": "test_comment",
+ "status": "enabled",
+ "indicator": "test_indicator",
+ "mitre_technique_id_and_name": "T1000,T1001",
+ "mitre_tactic_id_and_name": "TA0001,TA0002",
+ "xql_query": "test_query",
+ "is_enabled": "true",
+ "description": "test_description",
+ "alert_name": "test_alert_name",
+ "alert_category": "test_alert_category",
+ "alert_description": "test_alert_description",
+ "alert_fields": "field1,field2",
+ "execution_mode": "real_time",
+ "search_window": "1h",
+ "schedule": "daily",
+ "timezone": "UTC",
+ "schedule_linux": "* * * * *",
+ "suppression_enabled": "true",
+ "suppression_duration": "1h",
+ "suppression_fields": "field1",
+ "dataset": "xdr_data",
+ "user_defined_severity": "medium",
+ "user_defined_category": "category",
+ "mitre_defs_json": '{"tactic": "test"}',
+ "investigation_query_link": "link",
+ "drilldown_query_timeframe": "1h",
+ "mapping_strategy": "strategy",
+ "alert_domain": "domain",
+ }
+
+ filters = create_filters_for_bioc_and_correlation_rules(args)
+
+ assert {"field": "name", "operator": "EQ", "value": "test_name"} in filters
+ assert {"field": "severity", "operator": "EQ", "value": "SEV_040_HIGH"} in filters
+ assert {"field": "type", "operator": "EQ", "value": "bioc_type"} in filters
+ assert {"field": "is_xql", "operator": "EQ", "value": True} in filters
+ assert {"field": "comment", "operator": "EQ", "value": "test_comment"} in filters
+ assert {"field": "status", "operator": "EQ", "value": "enabled"} in filters
+ assert {"field": "indicator", "operator": "EQ", "value": "test_indicator"} in filters
+ assert {"field": "mitre_technique_id_and_name", "operator": "EQ", "value": ["T1000", "T1001"]} in filters
+ assert {"field": "mitre_tactic_id_and_name", "operator": "EQ", "value": ["TA0001", "TA0002"]} in filters
+ assert {"field": "xql_query", "operator": "EQ", "value": "test_query"} in filters
+ assert {"field": "is_enabled", "operator": "EQ", "value": True} in filters
+ assert {"field": "description", "operator": "EQ", "value": "test_description"} in filters
+ assert {"field": "alert_name", "operator": "EQ", "value": "test_alert_name"} in filters
+ assert {"field": "alert_category", "operator": "EQ", "value": "test_alert_category"} in filters
+ assert {"field": "alert_description", "operator": "EQ", "value": "test_alert_description"} in filters
+ assert {"field": "alert_fields", "operator": "EQ", "value": ["field1", "field2"]} in filters
+ assert {"field": "execution_mode", "operator": "EQ", "value": "real_time"} in filters
+ assert {"field": "search_window", "operator": "EQ", "value": "1h"} in filters
+ assert {"field": "schedule", "operator": "EQ", "value": "daily"} in filters
+ assert {"field": "timezone", "operator": "EQ", "value": "UTC"} in filters
+ assert {"field": "schedule_linux", "operator": "EQ", "value": "* * * * *"} in filters
+ assert {"field": "suppression_enabled", "operator": "EQ", "value": True} in filters
+ assert {"field": "suppression_duration", "operator": "EQ", "value": "1h"} in filters
+ assert {"field": "suppression_fields", "operator": "EQ", "value": "field1"} in filters
+ assert {"field": "dataset", "operator": "EQ", "value": "xdr_data"} in filters
+ assert {"field": "user_defined_severity", "operator": "EQ", "value": "medium"} in filters
+ assert {"field": "user_defined_category", "operator": "EQ", "value": "category"} in filters
+ assert {"field": "mitre_defs", "operator": "EQ", "value": {"tactic": "test"}} in filters
+ assert {"field": "investigation_query_link", "operator": "EQ", "value": "link"} in filters
+ assert {"field": "drilldown_query_timeframe", "operator": "EQ", "value": "1h"} in filters
+ assert {"field": "mapping_strategy", "operator": "EQ", "value": "strategy"} in filters
+ assert {"field": "alert_domain", "operator": "EQ", "value": "domain"} in filters
diff --git a/Packs/CortexXDR/Integrations/CortexXDRIR/README.md b/Packs/CortexXDR/Integrations/CortexXDRIR/README.md
index 90f71626678c..ea27ec036a88 100644
--- a/Packs/CortexXDR/Integrations/CortexXDRIR/README.md
+++ b/Packs/CortexXDR/Integrations/CortexXDRIR/README.md
@@ -4122,3 +4122,285 @@ Gets a list of existing API keys.
| PaloAltoNetworksXDR.APIKeyData.id | String | The API key ID. |
| PaloAltoNetworksXDR.APIKeyData.roles | String | The roles associated with the API key. |
| PaloAltoNetworksXDR.APIKeyData.expiration | Date | The expiration date of the API key. |
+
+### xdr-bioc-list
+
+***
+Returns a list of BIOCs.
+
+#### Base Command
+
+`xdr-bioc-list`
+
+#### Input
+
+| **Argument Name** | **Description** | **Required** |
+| --- | --- | --- |
+| name | The BIOC name to filter by. Can filter only by one name. | Optional |
+| severity | The BIOC severity to filter by. Possible values are: info, low, medium, high. | Optional |
+| type | The BIOC type to filter by. Possible values are: other, persistence, evasion, tampering, file_type_obfuscation, privilege_escalation, credential_access, lateral_movement, execution, collection, exfiltration, infiltration, dropper, file_privilege_manipulation, reconnaissance, discovery. | Optional |
+| is_xql | Whether the BIOC is XQL. Possible values are: true, false. | Optional |
+| comment | The BIOC comment to filter by. | Optional |
+| status | The BIOC status to filter by. Can be one of the following: enabled, disabled. Possible values are: enabled, disabled. | Optional |
+| indicator | The BIOC indicator to filter by. | Optional |
+| mitre_technique_id_and_name | The MITRE technique ID and name. Must be in format 'ID - Name', for example: ['T1566 - Phishing']. | Optional |
+| mitre_tactic_id_and_name | The MITRE technique ID and name. Must be in format 'ID - Name', for example: ['T1566 - Phishing']. | Optional |
+| extra_data | Whether to return extended data. Possible values are: true, false. | Optional |
+| limit | Maximum number of results to return. | Optional |
+| page_size | Page size. | Optional |
+| page | Page number. | Optional |
+
+#### Context Output
+
+| **Path** | **Type** | **Description** |
+| --- | --- | --- |
+| PaloAltoNetworksXDR.BIOC.rule_id | String | BIOC rule id. |
+| PaloAltoNetworksXDR.BIOC.name | String | BIOC name. |
+| PaloAltoNetworksXDR.BIOC.type | String | BIOC type. |
+| PaloAltoNetworksXDR.BIOC.severity | String | BIOC severity. |
+| PaloAltoNetworksXDR.BIOC.status | String | BIOC status. |
+| PaloAltoNetworksXDR.BIOC.is_xql | Bool | Whether the BIOC is XQL. |
+| PaloAltoNetworksXDR.BIOC.comment | String | The BIOC comment. |
+| PaloAltoNetworksXDR.BIOC.indicator | Unknown | The BIOC indicator. |
+
+### xdr-bioc-create
+
+***
+Creates a new BIOC.
+
+#### Base Command
+
+`xdr-bioc-create`
+
+#### Input
+
+| **Argument Name** | **Description** | **Required** |
+| --- | --- | --- |
+| name | The BIOC name. | Required |
+| severity | The BIOC severity. Possible values are: info, low, medium, high. | Required |
+| type | The BIOC type. Possible values are: other, persistence, evasion, tampering, file_type_obfuscation, privilege_escalation, credential_access, lateral_movement, execution, collection, exfiltration, infiltration, dropper, file_privilege_manipulation, reconnaissance, discovery. | Required |
+| is_xql | Whether the new BIOC is XQL. Possible values are: true, false. | Optional |
+| comment | The BIOC comment. | Required |
+| status | The BIOC status. Possible values are: enabled, disabled. | Required |
+| indicator | The BIOC indicator,
for example: '{\"runOnCGO\":true,\"investigationType\":\"FILE_EVENT\",\"investigation\":{\"FILE_EVENT\":{\"filter\":{\"AND\":[{\"SEARCH_FIELD\":\"action_file_name\",\"SEARCH_TYPE\":\"EQ\",\"SEARCH_VALUE\":\"testfile.exe\"}]}}}}'.
For more information please refer to the documentation https://docs-cortex.paloaltonetworks.com/r/Cortex-XDR-Platform-APIs/Insert-or-update-BIOCs. | Required |
+| mitre_technique_id_and_name | The MITRE technique ID and name. Must be in format 'ID - Name', for example: ['T1566 - Phishing']. | Optional |
+| mitre_tactic_id_and_name | The MITRE tactic ID and name. Must be in format 'ID - Name', for example: ['TA0001 - Initial Access']. | Optional |
+
+#### Context Output
+
+| **Path** | **Type** | **Description** |
+| --- | --- | --- |
+| PaloAltoNetworksXDR.BIOC.name | String | BIOC name. |
+| PaloAltoNetworksXDR.BIOC.type | String | BIOC type. |
+| PaloAltoNetworksXDR.BIOC.severity | String | BIOC severity. |
+| PaloAltoNetworksXDR.BIOC.status | String | BIOC status. |
+
+### xdr-bioc-update
+
+***
+Updates an existing BIOC.
+
+#### Base Command
+
+`xdr-bioc-update`
+
+#### Input
+
+| **Argument Name** | **Description** | **Required** |
+| --- | --- | --- |
+| rule_id | BIOC rule ID. | Required |
+| name | BIOC name. | Required |
+| severity | BIOC severity. Possible values are: info, low, medium, high. | Required |
+| type | BIOC type. Possible values are: other, persistence, evasion, tampering, file_type_obfuscation, privilege_escalation, credential_access, lateral_movement, execution, collection, exfiltration, infiltration, dropper, file_privilege_manipulation, reconnaissance, discovery. | Required |
+| is_xql | Whether the BIOC is XQL. Possible values are: true, false. | Optional |
+| comment | BIOC comment. | Required |
+| status | BIOC status. Possible values are: enabled, disabled. | Required |
+| indicator | The BIOC indicator,
for example: '{\"runOnCGO\":true,\"investigationType\":\"FILE_EVENT\",\"investigation\":{\"FILE_EVENT\":{\"filter\":{\"AND\":[{\"SEARCH_FIELD\":\"action_file_name\",\"SEARCH_TYPE\":\"EQ\",\"SEARCH_VALUE\":\"testfile.exe\"}]}}}}'.
For more information please refer to the documentation https://docs-cortex.paloaltonetworks.com/r/Cortex-XDR-Platform-APIs/Insert-or-update-BIOCs. | Required |
+| mitre_technique_id_and_name | The MITRE technique ID and name. Must be in format 'ID - Name', for example: ['T1566 - Phishing']. | Optional |
+| mitre_tactic_id_and_name | The MITRE tactic ID and name. Must be in format 'ID - Name', for example: ['TA0001 - Initial Access']. | Optional |
+
+#### Context Output
+
+| **Path** | **Type** | **Description** |
+| --- | --- | --- |
+| PaloAltoNetworksXDR.BIOC.name | String | BIOC name. |
+| PaloAltoNetworksXDR.BIOC.type | String | BIOC type. |
+| PaloAltoNetworksXDR.BIOC.severity | String | BIOC severity. |
+| PaloAltoNetworksXDR.BIOC.status | String | BIOC status. |
+
+### xdr-bioc-delete
+
+***
+Deletes a BIOC.
+
+#### Base Command
+
+`xdr-bioc-delete`
+
+#### Input
+
+| **Argument Name** | **Description** | **Required** |
+| --- | --- | --- |
+| name | BIOC name. | Optional |
+| severity | BIOC severity. Possible values are: info, low, medium, high. | Optional |
+| type | BIOC type. Possible values are: other, persistence, evasion, tampering, file_type_obfuscation, privilege_escalation, credential_access, lateral_movement, execution, collection, exfiltration, infiltration, dropper, file_privilege_manipulation, reconnaissance, discovery. | Optional |
+| is_xql | Whether the BIOC is XQL. Possible values are: true, false. | Optional |
+| comment | BIOC comment. | Optional |
+| indicator | BIOC indicator. | Optional |
+| mitre_technique_id_and_name | The MITRE technique ID and name. Must be in format 'ID - Name', for example: ['T1566 - Phishing']. | Optional |
+| mitre_tactic_id_and_name | The MITRE tactic ID and name. Must be in format 'ID - Name', for example: ['TA0001 - Initial Access']. | Optional |
+
+#### Context Output
+
+There is no context output for this command.
+
+### xdr-correlation-rule-list
+
+***
+Returns a list of correlation rules.
+
+#### Base Command
+
+`xdr-correlation-rule-list`
+
+#### Input
+
+| **Argument Name** | **Description** | **Required** |
+| --- | --- | --- |
+| name | Correlation rule name. | Optional |
+| severity | Correlation rule severity. Possible values are: info, low, medium, high. | Optional |
+| xql_query | Correlation rule XQL query. | Optional |
+| is_xql | Whether the correlation rule is XQL. Possible values are: true, false. | Optional |
+| dataset | Correlation rule dataset. | Optional |
+| alert_name | Alert name. | Optional |
+| alert_category | Alert category. | Optional |
+| alert_fields | Alert fields. Can be a string or a dictionary. | Optional |
+| alert_domain | Alert domain. | Optional |
+| filter_json | Filter JSON. | Optional |
+| extra_data | Whether to return extended view. Possible values are: true, false. | Optional |
+| limit | Maximum number of results to return. | Optional |
+| page_size | Page size. | Optional |
+| page | Page number. | Optional |
+
+#### Context Output
+
+| **Path** | **Type** | **Description** |
+| --- | --- | --- |
+| PaloAltoNetworksXDR.CorrelationRule.id | String | Correlation rule ID. |
+| PaloAltoNetworksXDR.CorrelationRule.name | String | Correlation rule name. |
+| PaloAltoNetworksXDR.CorrelationRule.description | String | Correlation rule description. |
+| PaloAltoNetworksXDR.CorrelationRule.is_enabled | Boolean | Whether the correlation rule is enabled. |
+
+### xdr-correlation-rule-create
+
+***
+Creates a new correlation rule.
+
+#### Base Command
+
+`xdr-correlation-rule-create`
+
+#### Input
+
+| **Argument Name** | **Description** | **Required** |
+| --- | --- | --- |
+| name | The correlation rule name. Example: name="This is a CR test 6". | Required |
+| severity | The correlation rule severity. Example: severity=low. Possible values are: info, low, medium, high. | Required |
+| xql_query | The correlation rule XQL query. Example: xql_query="dataset = xdr_data \| limit 1". | Required |
+| is_enabled | Whether the rule is enabled. Example: is_enabled=true. Possible values are: true, false. | Required |
+| alert_category | The alert category. Example: alert_category=dropper. Possible values are: other, persistence, evasion, tampering, file_type_obfuscation, privilege_escalation, credential_access, lateral_movement, execution, collection, exfiltration, infiltration, dropper, file_privilege_manipulation, reconnaissance, discovery. | Required |
+| execution_mode | The rule execution mode. Example: execution_mode=scheduled. Possible values are: scheduled, real_time. | Required |
+| timezone | The correlation rule timezone. Example: timezone="Asia/Jerusalem". | Required |
+| mapping_strategy | The rule mapping strategy. Example: mapping_strategy=auto. Possible values are: auto, custom. | Required |
+| description | The correlation rule description. | Optional |
+| alert_name | The alert name. | Optional |
+| alert_description | The alert description. | Optional |
+| alert_fields | Alert fields (string or dictionary). | Optional |
+| search_window | The search window timeframe. Example: search_window="1 hours". | Optional |
+| schedule | The correlation rule schedule. Example: schedule="10 minutes". | Optional |
+| schedule_linux | Linux scheduling for the rule. Example: schedule_linux="*/10* ** *". | Optional |
+| suppression_enabled | Whether suppression is enabled. Possible values are: true, false. | Optional |
+| suppression_duration | Duration of correlation rule suppression. | Optional |
+| suppression_fields | Suppression fields. | Optional |
+| dataset | The correlation rule dataset. Example: dataset=alerts. | Optional |
+| user_defined_severity | User-defined severity. | Optional |
+| user_defined_category | User-defined category. | Optional |
+| mitre_defs_json | MITRE definitions. | Optional |
+| investigation_query_link | Investigation query link. | Optional |
+| drilldown_query_timeframe | The drilldown query timeframe. Example: drilldown_query_timeframe="ALERT". | Optional |
+
+#### Context Output
+
+| **Path** | **Type** | **Description** |
+| --- | --- | --- |
+| PaloAltoNetworksXDR.CorrelationRule.id | String | Correlation rule ID. |
+| PaloAltoNetworksXDR.CorrelationRule.name | String | Correlation rule name. |
+| PaloAltoNetworksXDR.CorrelationRule.description | String | Correlation rule description. |
+| PaloAltoNetworksXDR.CorrelationRule.is_enabled | Boolean | Whether the correlation rule is enabled. |
+
+### xdr-correlation-rule-update
+
+***
+Updates an existing correlation rule.
+
+#### Base Command
+
+`xdr-correlation-rule-update`
+
+#### Input
+
+| **Argument Name** | **Description** | **Required** |
+| --- | --- | --- |
+| rule_id | Correlation rule ID. | Required |
+| name | The correlation rule name. Example: name="This is a CR test 6". | Required |
+| severity | The correlation rule severity. Example: severity=low. Possible values are: info, low, medium, high. | Required |
+| xql_query | The correlation rule XQL query. Example: xql_query="dataset = xdr_data \| limit 1". | Required |
+| is_enabled | Whether the rule is enabled. Example: is_enabled=true. Possible values are: true, false. | Required |
+| alert_category | The alert category. Example: alert_category=dropper. Possible values are: other, persistence, evasion, tampering, file_type_obfuscation, privilege_escalation, credential_access, lateral_movement, execution, collection, exfiltration, infiltration, dropper, file_privilege_manipulation, reconnaissance, discovery. | Required |
+| execution_mode | The rule execution mode. Example: execution_mode=scheduled. Possible values are: scheduled, real_time. | Required |
+| timezone | The correlation rule timezone. Example: timezone="Asia/Jerusalem". | Required |
+| mapping_strategy | The rule mapping strategy. Example: mapping_strategy=auto. Possible values are: auto, custom. | Required |
+| description | The correlation rule description. | Optional |
+| alert_name | The alert name. | Optional |
+| alert_description | The alert description. | Optional |
+| alert_fields | Alert fields (string or dictionary). | Optional |
+| search_window | The search window timeframe. Example: search_window="1 hours". | Optional |
+| schedule | The correlation rule schedule. Example: schedule="10 minutes". | Optional |
+| schedule_linux | Linux scheduling for the rule. Example: schedule_linux="*/10* ** *". | Optional |
+| suppression_enabled | Whether suppression is enabled. Possible values are: true, false. | Optional |
+| suppression_duration | Duration of correlation rule suppression. | Optional |
+| suppression_fields | Suppression fields. | Optional |
+| dataset | The correlation rule dataset. Example: dataset=alerts. | Required |
+| user_defined_severity | User-defined severity. | Optional |
+| user_defined_category | User-defined category. | Optional |
+| mitre_defs_json | MITRE definitions. | Optional |
+| investigation_query_link | Investigation query link. | Optional |
+| drilldown_query_timeframe | The drilldown query timeframe. Example: drilldown_query_timeframe="ALERT". | Optional |
+
+#### Context Output
+
+| **Path** | **Type** | **Description** |
+| --- | --- | --- |
+| PaloAltoNetworksXDR.CorrelationRule.id | String | Correlation rule ID. |
+| PaloAltoNetworksXDR.CorrelationRule.name | String | Correlation rule name. |
+| PaloAltoNetworksXDR.CorrelationRule.description | String | Correlation rule description. |
+| PaloAltoNetworksXDR.CorrelationRule.is_enabled | Boolean | Whether the correlation rule is enabled. |
+
+### xdr-correlation-rule-delete
+
+***
+Deletes correlation rules.
+
+#### Base Command
+
+`xdr-correlation-rule-delete`
+
+#### Input
+
+| **Argument Name** | **Description** | **Required** |
+| --- | --- | --- |
+| rule_id | Correlation rule ID. | Required |
+
+#### Context Output
+
+There is no context output for this command.
diff --git a/Packs/CortexXDR/Integrations/CortexXDRIR/command_examples.txt b/Packs/CortexXDR/Integrations/CortexXDRIR/command_examples.txt
index 015bfe520045..6266f168b98d 100644
--- a/Packs/CortexXDR/Integrations/CortexXDRIR/command_examples.txt
+++ b/Packs/CortexXDR/Integrations/CortexXDRIR/command_examples.txt
@@ -61,4 +61,14 @@
!xdr-asset-group-list sort_field="XDM.ASSET_GROUP.LAST_UPDATE_TIME" page=1 page_size=3
!xdr-asset-group-create group_name="assetGroupTest" group_type=Dynamic membership_predicate_json="{\"AND\":[{\"SEARCH_FIELD\":\"xdm.asset.type.class\",\"SEARCH_TYPE\":\"NEQ\",\"SEARCH_VALUE\":\"Other\"}]}"
!xdr-asset-group-update group_id=1 group_type=Dynamic group_description=test group_name="Test" membership_predicate_json="{\"AND\":[{\"SEARCH_FIELD\":\"xdm.asset.provider\",\"SEARCH_TYPE\":\"NEQ\",\"SEARCH_VALUE\":\"Other\"}]}"
-!xdr-asset-group-delete group_id=1
\ No newline at end of file
+!xdr-asset-group-delete group_id=1
+!xdr-bioc-list severity=high type=collection
+!xdr-bioc-create name="TestBIOC" severity=high mitre_tactic_id_and_name="['TA0002 - Execution']" mitre_technique_id_and_name="['T1059 - Command and Scripting Interpreter']" indicator="{\"runOnCGO\":true,\"investigationType\":\"FILE_EVENT\",\"investigation\":{\"FILE_EVENT\":{\"filter\":{\"AND\":[{\"SEARCH_FIELD\":\"action_file_name\",\"SEARCH_TYPE\":\"EQ\",\"SEARCH_VALUE\":\"testfile.exe\"}]}}}}" type=collection status=enabled comment="Test"
+!xdr-bioc-update name="TestBIOC" severity=high mitre_tactic_id_and_name="['TA0002 - Execution']" mitre_technique_id_and_name="['T1059 - Command and Scripting Interpreter']" indicator="{\"runOnCGO\":true,\"investigationType\":\"FILE_EVENT\",\"investigation\":{\"FILE_EVENT\":{\"filter\":{\"AND\":[{\"SEARCH_FIELD\":\"action_file_name\",\"SEARCH_TYPE\":\"EQ\",\"SEARCH_VALUE\":\"testfile.exe\"}]}}}}" type=collection status=enabled comment="Test"
+!xdr-bioc-delete type=collection name="TestBIOC"
+!xdr-correlation-rule-list extra_data=true
+!xdr-correlation-rule-create name="CR test" severity=low alert_category=dropper execution_mode=scheduled mapping_strategy=auto xql_query="dataset = xdr_data | limit 1" dataset=alerts alert_name="Test" is_enabled=true drilldown_query_timeframe="ALERT" investigation_query_link="dataset = xdr_data | limit 1" schedule="10 minutes" schedule_linux="*/10 * * * *" search_window="1 hours" suppression_duration="1 hours" suppression_enabled=true suppression_fields="event_type" timezone="Asia/Jerusalem"
+!xdr-correlation-rule-update name="CR test" severity=low alert_category=dropper execution_mode=scheduled mapping_strategy=auto xql_query="dataset = xdr_data | limit 1" dataset=alerts alert_name="Test" is_enabled=true drilldown_query_timeframe="ALERT" investigation_query_link="dataset = xdr_data | limit 1" schedule="10 minutes" schedule_linux="*/10 * * * *" search_window="1 hours" suppression_duration="1 hours" suppression_enabled=true suppression_fields="event_type" timezone="Asia/Jerusalem"
+!xdr-correlation-rule-delete rule_id=14,15
+
+
diff --git a/Packs/CortexXDR/ReleaseNotes/6_3_5.md b/Packs/CortexXDR/ReleaseNotes/6_3_5.md
new file mode 100644
index 000000000000..9b18ea6cd9cf
--- /dev/null
+++ b/Packs/CortexXDR/ReleaseNotes/6_3_5.md
@@ -0,0 +1,13 @@
+
+#### Integrations
+
+##### Palo Alto Networks Cortex XDR - Investigation and Response
+
+- Added support for the **xdr-bioc-create** command which creates a new BIOC.
+- Added support for the **xdr-bioc-update** command which updates an existing BIOC.
+- Added support for the **xdr-bioc-delete** command which deletes a BIOC.
+- Added support for the **xdr-bioc-list** command which returns a list of BIOCs.
+- Added support for the **xdr-correlation-rule-list** command which returns a list of correlation rules.
+- Added support for the **xdr-correlation-rule-update** command which updates an existing correlation rule.
+- Added support for the **xdr-correlation-rule-delete** command which deletes correlation rules.
+- Added support for the **xdr-correlation-rule-create** command which creates a new correlation rule.
diff --git a/Packs/CortexXDR/pack_metadata.json b/Packs/CortexXDR/pack_metadata.json
index 8c79f76bf836..bc4010a16d6b 100644
--- a/Packs/CortexXDR/pack_metadata.json
+++ b/Packs/CortexXDR/pack_metadata.json
@@ -2,7 +2,7 @@
"name": "Cortex XDR by Palo Alto Networks",
"description": "Automates Cortex XDR incident response, and includes custom Cortex XDR incident views and layouts to aid analyst investigations.",
"support": "xsoar",
- "currentVersion": "6.3.4",
+ "currentVersion": "6.3.5",
"author": "Cortex XSOAR",
"url": "https://www.paloaltonetworks.com/cortex",
"email": "",