Unit 42 Intelligence
Enrich indicators with Unit 42 threat intelligence context including verdicts, threat object associations, and relationships.
Data Enrichment & Threat Intelligence · Unit 42 Threat Intelligence by Palo Alto Networks
Details
| ID | Unit 42 Intelligence |
|---|---|
| Provider | Palo Alto Networks |
| Category | Data Enrichment & Threat Intelligence |
| From Version | 6.10.0 |
| Docker Image | demisto/python3:3.12.13.10404775 |
| Supported Modules | Agentix Cortex Cloud EDR XSIAM Cloud Runtime Security Cloud Posture Security |
README
Enrich indicators with Unit 42 threat intelligence context including verdicts, threat object associations, and relationships.
Configure Unit 42 Intelligence in Cortex
| Parameter | Description | Required |
|---|---|---|
| Source Reliability | Reliability of the source providing the intelligence data | True |
| Create relationships | Create relationships between indicators and threat objects | False |
| Create threat objects as separate indicators | Whether to create threat objects (malware families, actors, campaigns, etc.) as separate XSOAR indicators | False |
| Trust any certificate (not secure) | False | |
| Use system proxy settings | False |
Commands
You can execute these commands from the CLI, as part of an automation, or in a playbook.
After you successfully execute a command, a DBot message appears in the War Room with the command details.
ip
Enrich an IP address with Unit 42 threat intelligence context.
Base Command
ip
Input
| Argument Name | Description | Required |
|---|---|---|
| ip | The IP address to enrich. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| IP.Address | String | The IP address. |
| IP.Malicious.Vendor | String | The vendor reporting the IP as malicious. |
| IP.Malicious.Description | String | Description of the malicious IP. |
| DBotScore.Indicator | String | The indicator that was tested. |
| DBotScore.Type | String | The indicator type. |
| DBotScore.Vendor | String | The vendor used to calculate the score. |
| DBotScore.Score | Number | The actual score. |
| DBotScore.Reliability | String | Reliability of the source providing the intelligence data. |
| Unit42.IP.Value | String | The IP address. |
| Unit42.IP.Type | String | The indicator type. |
| Unit42.IP.Counts | Unknown | Counts. |
| Unit42.IP.Verdict | String | The verdict for the IP. |
| Unit42.IP.VerdictCategory | Unknown | The verdict category. |
| Unit42.IP.FirstSeen | Date | First seen date. |
| Unit42.IP.LastSeen | Date | Last seen date. |
| Unit42.IP.SeenBy | Unknown | Sources that have seen this IP. |
| Unit42.IP.EnrichedThreatObjectAssociation | Unknown | Enriched threat object association. |
Command example
!ip ip="8.8.8.8"
Context Example
{
"Unit42.IP": {
"Counts": [
{
"count_type": "wf_sample",
"count_values": {
"benign": 246022,
"grayware": 214,
"malware": 3176800
}
}
],
"EnrichedThreatObjectAssociation": null,
"FirstSeen": "",
"LastSeen": "",
"SeenBy": [
"wf_sample"
],
"Type": "IP",
"Value": "8.8.8.8",
"Verdict": "malicious",
"VerdictCategory": null
}
}
Human Readable Output
Unit 42 Intelligence results for IP: 8.8.8.8
Value Verdict Verdict Category Seen By First Seen Last Seen 8.8.8.8 malicious wf_sample
domain
Enrich a domain with Unit 42 threat intelligence context.
Base Command
domain
Input
| Argument Name | Description | Required |
|---|---|---|
| domain | Domain to enrich. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| Domain.Name | String | The domain name. |
| Domain.Malicious.Vendor | String | The vendor reporting the domain as malicious. |
| Domain.Malicious.Description | String | Description of the malicious domain. |
| DBotScore.Indicator | String | The indicator that was tested. |
| DBotScore.Type | String | The indicator type. |
| DBotScore.Vendor | String | The vendor used to calculate the score. |
| DBotScore.Score | Number | The actual score. |
| DBotScore.Reliability | String | Reliability of the source providing the intelligence data. |
| Unit42.Domain.Value | String | The domain name. |
| Unit42.Domain.Type | String | The indicator type. |
| Unit42.Domain.Counts | Unknown | Counts. |
| Unit42.Domain.Verdict | String | The verdict for the domain. |
| Unit42.Domain.VerdictCategory | Unknown | The verdict category. |
| Unit42.Domain.FirstSeen | Date | First seen date. |
| Unit42.Domain.LastSeen | Date | Last seen date. |
| Unit42.Domain.SeenBy | Unknown | Sources that have seen this domain. |
| Unit42.Domain.EnrichedThreatObjectAssociation | Unknown | Enriched threat object association. |
Command example
!domain domain="example.com"
Context Example
{
"Unit42.Domain":{
"Counts": null,
"EnrichedThreatObjectAssociation": null,
"FirstSeen": "",
"LastSeen": "",
"SeenBy": null,
"Type": "Domain",
"Value": "example.com",
"Verdict": "benign",
"VerdictCategory": [
"allowlist_dict_dga"
]
}
}
Human Readable Output
Unit 42 Intelligence results for Domain: example.com
Value Verdict Verdict Category Seen By First Seen Last Seen example.com benign allowlist_dict_dga wf_sample
url
Enrich a URL with Unit 42 threat intelligence context.
Base Command
url
Input
| Argument Name | Description | Required |
|---|---|---|
| url | URL to enrich. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| URL.Data | String | The URL. |
| URL.Malicious.Vendor | String | The vendor reporting the URL as malicious. |
| URL.Malicious.Description | String | Description of the malicious URL. |
| DBotScore.Indicator | String | The indicator that was tested. |
| DBotScore.Type | String | The indicator type. |
| DBotScore.Vendor | String | The vendor used to calculate the score. |
| DBotScore.Score | Number | The actual score. |
| DBotScore.Reliability | String | Reliability of the source providing the intelligence data. |
| Unit42.URL.Value | String | The URL. |
| Unit42.URL.Type | String | The indicator type. |
| Unit42.URL.Counts | Unknown | Counts. |
| Unit42.URL.Verdict | String | The verdict for the URL. |
| Unit42.URL.VerdictCategory | Unknown | The verdict category. |
| Unit42.URL.FirstSeen | Date | First seen date. |
| Unit42.URL.LastSeen | Date | Last seen date. |
| Unit42.URL.SeenBy | Unknown | Sources that have seen this URL. |
| Unit42.URL.EnrichedThreatObjectAssociation | Unknown | Enriched threat object association. |
Command example
!url url="https://en.wikipedia.org/wiki/URL"
Context Example
{
"Unit42.URL": {
"Counts": [
{
"count_type": "wf_sample",
"count_values": {
"benign": 97,
"grayware": 0,
"malware": 0
}
}
],
"EnrichedThreatObjectAssociation": null,
"FirstSeen": "",
"LastSeen": "",
"SeenBy": [
"wf_sample"
],
"Type": "URL",
"Value": "https://en.wikipedia.org/wiki/URL",
"Verdict": "unknown",
"VerdictCategory": null
}
}
Human Readable Output
Unit 42 Intelligence results for URL: https://en.wikipedia.org/wiki/URL
Value Verdict Verdict Category Seen By First Seen Last Seen https://en.wikipedia.org/wiki/URL unknown wf_sample
file
Enrich a file hash with Unit 42 threat intelligence context.
Base Command
file
Input
| Argument Name | Description | Required |
|---|---|---|
| file | File hash to enrich (SHA256). | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| File.MD5 | String | The MD5 hash of the file. |
| File.SHA1 | String | The SHA1 hash of the file. |
| File.SHA256 | String | The SHA256 hash of the file. |
| File.Malicious.Vendor | String | The vendor reporting the file as malicious. |
| File.Malicious.Description | String | Description of the malicious file. |
| DBotScore.Indicator | String | The indicator that was tested. |
| DBotScore.Type | String | The indicator type. |
| DBotScore.Vendor | String | The vendor used to calculate the score. |
| DBotScore.Score | Number | The actual score. |
| DBotScore.Reliability | String | Reliability of the source providing the intelligence data. |
| Unit42.File.Value | String | The file hash. |
| Unit42.File.Type | String | The indicator type. |
| Unit42.File.Counts | Unknown | Counts. |
| Unit42.File.Verdict | String | The verdict for the file. |
| Unit42.File.VerdictCategory | Unknown | The verdict category. |
| Unit42.File.FirstSeen | Date | First seen date. |
| Unit42.File.LastSeen | Date | Last seen date. |
| Unit42.File.SeenBy | Unknown | Sources that have seen this file. |
| Unit42.File.EnrichedThreatObjectAssociation | Unknown | Enriched threat object association. |
Command example
!file file="123456abcdef"
Context Example
{
"Unit42.File": {
"Counts": [
{
"count_type": "wf_sample",
"count_values": {
"benign": 0,
"grayware": 0,
"malware": 3
}
}
],
"EnrichedThreatObjectAssociation": null,
"FirstSeen": "",
"LastSeen": "",
"SeenBy": [
"wf_sample"
],
"Type": "File",
"Value": "123456abcdef",
"Verdict": "malicious",
"VerdictCategory": null
}
}
Human Readable Output
Unit 42 Intelligence results for File: 123456abcdef
Value Verdict Verdict Category Seen By First Seen Last Seen 123456abcdef malicious wf_sample
Known Limitations
URL Enrichment Format Requirements
To ensure successful URL enrichment and avoid HTTP 400 errors, follow these guidelines:
URL Length
- Maximum recommended length: 2000 characters
- Observed maximum length for URL arguments in similar indicator submission commands within the Cortex platform
Special Characters
- URLs must be properly encoded
- Characters like commas (
,), less-than (<), and greater-than (>) can cause parsing conflicts or API request failures (Status 400) - Remove or URL-encode these characters before submission
Comma Handling
- If a URL contains a comma, the playbook task supplying the input must enclose the URL within a JSON list format to ensure it is treated as a single string, even if it is the only URL
- Example:
["https://example.com/path?param=value,value2"]
General Guidelines
- Ensure URLs are valid and well-formed
- Use proper URL encoding for special characters
- Test URLs with special characters in a non-production environment first
Configuration parameters
integration_reliability— Source Reliabilitycreate_relationships— Create relationshipscreate_threat_object_indicators— Create threat objects as separate indicatorsinsecure— Trust any certificate (not secure)proxy— Use system proxy settings
Commands (4)
-
domainEnrich a domain with Unit 42 threat intelligence context.
-
fileEnrich a file hash with Unit 42 threat intelligence context.
-
ipEnrich an IP address with Unit 42 threat intelligence context.
-
urlEnrich a URL with Unit 42 threat intelligence context.
import demistomock as demisto # noqa: F401 from CommonServerPython import * # noqa: F401 import urllib3 from typing import Any # Disable insecure warnings urllib3.disable_warnings() #### CONSTANTS #### INTEGRATION_NAME = "Unit 42 Intelligence" # API endpoints SERVER_URL = "https://prod-us.tas.crtx.paloaltonetworks.com" LOOKUP_ENDPOINT = "/api/v1/lookups/indicator/{indicator_type}/{indicator_value}" # Retry configuration RETRY_COUNT = 5 STATUS_CODES_TO_RETRY = list(range(429, 600)) # Score mappings VERDICT_TO_SCORE = { "malicious": Common.DBotScore.BAD, "suspicious": Common.DBotScore.SUSPICIOUS, "benign": Common.DBotScore.GOOD, "unknown": Common.DBotScore.NONE, } # Indicator type mappings INDICATOR_TYPE_MAPPING = { "ip": FeedIndicatorType.IP, "domain": FeedIndicatorType.Domain, "url": FeedIndicatorType.URL, "file": FeedIndicatorType.File, "filehash_sha256": FeedIndicatorType.File, "exploit": FeedIndicatorType.CVE, "vulnerability": FeedIndicatorType.CVE, "malware_family": ThreatIntel.ObjectsNames.MALWARE, "grayware": ThreatIntel.ObjectsNames.MALWARE, "actor": ThreatIntel.ObjectsNames.THREAT_ACTOR, "threat_actor": ThreatIntel.ObjectsNames.THREAT_ACTOR, "campaign": ThreatIntel.ObjectsNames.CAMPAIGN, "attack pattern": ThreatIntel.ObjectsNames.ATTACK_PATTERN, "attack_pattern": ThreatIntel.ObjectsNames.ATTACK_PATTERN, "technique": ThreatIntel.ObjectsNames.ATTACK_PATTERN, "malicious_behavior": ThreatIntel.ObjectsNames.ATTACK_PATTERN, "malicious behavior": ThreatIntel.ObjectsNames.ATTACK_PATTERN, "malicious_tool": ThreatIntel.ObjectsNames.TOOL, } # Define valid regions enum VALID_REGIONS = { "australia and oceania": "Australia And Oceania", "antarctica": "Antarctica", "north america": "North America", "south asia": "South Asia", "europe": "Europe", "central america and the caribbean": "Central America And The Caribbean", "africa": "Africa", "east and southeast asia": "East And Southeast Asia", "middle east": "Middle East", "central asia": "Central Asia", "south america": "South America", } #### HELPER FUNCTIONS #### def parse_url_list(url_input: str | list | None) -> list[str]: """ Parse URL input that may contain multiple URLs or a single URL with commas. This function intelligently splits URLs by detecting URL patterns to avoid incorrectly splitting URLs that contain commas in their parameters. Supports list inputs (from playbooks), None inputs, and newline separators. Also handles URLs wrapped in double-quote or single-quote characters (e.g. from email security tools or JSON extraction): "url1","url2" or 'url1','url2' is split and quotes are stripped. Args: url_input: String, list, or None containing one or more URLs Returns: List of individual URLs Examples: >>> parse_url_list("http://example.com,https://test.com") ['http://example.com', 'https://test.com'] >>> parse_url_list("https://fonts.googleapis.com/css?family=Roboto:100,100italic,200") ['https://fonts.googleapis.com/css?family=Roboto:100,100italic,200'] >>> parse_url_list("http://a.com/path?x=1,2,http://b.com") ['http://a.com/path?x=1,2', 'http://b.com'] >>> parse_url_list(["http://example.com", "https://test.com"]) ['http://example.com', 'https://test.com'] >>> parse_url_list(None) [] >>> parse_url_list("ftp://example.com,http://test.com") ['ftp://example.com', 'http://test.com'] >>> parse_url_list('"share.google/a","example.com/url?a=https://share.google/a"') ['share.google/a', 'example.com/url?a=https://share.google/a'] >>> parse_url_list("'https://example.com','https://test.com'") ['https://example.com', 'https://test.com'] """ if url_input is None: return [] # Handle list input from playbooks if isinstance(url_input, list): return [url.strip(" \"'") for url in argToList(url_input) if url and str(url).strip()] # Handle string input - split by newlines and intelligently by commas in one pass url_input = str(url_input).strip() if not url_input: return [] # Split on any of: # 1. "\s*,\s*" — comma between double-quoted URLs ('"url1", "url2"' => ["url1", "url2"]) # 2. '\s*,\s*' — comma between single-quoted URLs ("'url1', 'url2'" => ['url1', 'url2']) # 3. ,\s*(?=scheme://) — comma followed by a URL scheme ("http://a.com?x=1,2,http://b.com" => ["http://a.com?x=1,2", "http://b.com"]) # 4. \n — newline separator ("http://a.com/path \n http://b.com/path" => ["http://a.com/path", "http://b.com/path"]) # Alternatives 1 & 2 must come before 3 so that a comma inside a quoted URL's # query string (e.g. "url?a=https://other") is not split by the scheme lookahead. segments = re.split(r'"\s*,\s*"|' + r"'\s*,\s*'" + r"|,\s*(?=[a-zA-Z][a-zA-Z0-9+.-]*://)|\n", url_input) return [url for segment in segments if (url := segment.strip(" \"'"))] def unit42_error_handler(res: requests.Response): """ Custom error handler for Unit 42 API requests. Extracts and logs X-Request-ID header for failed requests (4xx/5xx errors). Args: res: Response object from failed request Returns: Error message string including X-Request-ID if available """ request_id = res.headers.get("X-Request-ID", "N/A") demisto.debug(f"{INTEGRATION_NAME} API Error - X-Request-ID: {request_id}, Status: {res.status_code}, URL: {res.url}") error_msg = f"Error in API request [Status: {res.status_code}]\n" error_msg += f"[X-Request-ID: {request_id}]\n" error_msg += f"Response text - {res.text}" return_error(error_msg) def encode_url_indicator(indicator_value: str) -> str: """ Double-encode URLs to handle special characters like < and > and URLs without schemes. URL fragments (the part after '#') are stripped before encoding because the Unit 42 TDP API rejects any URL that contains an encoded '#' (%23) in the indicator value. Args: indicator_value: The URL to encode Returns: Encoded URL string (fragment stripped) """ # Step 1: Ensure URL has a scheme for proper parsing # If no scheme is present, urlparse treats the entire URL as a path # which causes incorrect encoding (e.g., domain slashes get encoded) parsed_check = urllib.parse.urlparse(indicator_value) had_scheme = bool(parsed_check.scheme) url_to_parse = indicator_value if not had_scheme: url_to_parse = "http://" + indicator_value # Step 2: Parse the URL to separate components parsed = urllib.parse.urlparse(url_to_parse) # Step 3: Encode special characters in the path (keeping '/' safe) # This handles characters like ^, |, <, > in the path encoded_path = urllib.parse.quote(parsed.path, safe="/") if parsed.path else "" # Step 4: Encode special characters in the query string (keeping '=' and '&' safe) # This converts query=<test> to query=%3Ctest%3E encoded_query = urllib.parse.quote(parsed.query, safe="=&") if parsed.query else "" # Step 5: Reconstruct the URL with the encoded path and query, # dropping the fragment (as this is not relevant for threat Intelligence tools). temp_url = urllib.parse.urlunparse(parsed._replace(path=encoded_path, query=encoded_query, fragment="")) # Step 6: Remove the scheme if it was added for parsing # The API expects URLs without the scheme prefix if not had_scheme: temp_url = temp_url.replace("http://", "", 1) # Step 7: Final full URL encoding for the API request # This converts the entire URL including the already-encoded path and query # Example: example.com/search?query=%3Ctest%3E becomes # example.com%2Fsearch%3Fquery%3D%253Ctest%253E return urllib.parse.quote(temp_url, safe="") #### CLIENT CLASS #### class Client(BaseClient): """Client class to interact with Unit 42 Intelligence API""" def __init__( self, verify: bool, proxy: bool, reliability: str, ): headers = {"Authorization": f"Bearer {demisto.getLicenseID()}", "Content-Type": "application/json"} super().__init__(base_url=SERVER_URL, verify=verify, proxy=proxy, headers=headers) self.reliability = reliability def lookup_indicator(self, indicator_type: str, indicator_value: str) -> requests.Response: """ Lookup an indicator in Unit 42 Intelligence Args: indicator_type: Type of indicator (ip, domain, url, filehash_sha256) indicator_value: Value of the indicator Returns: requests.Response object """ if indicator_type.lower() == "url": indicator_value = encode_url_indicator(indicator_value=indicator_value) endpoint = LOOKUP_ENDPOINT.format(indicator_type=indicator_type, indicator_value=indicator_value) return self._http_request( method="GET", url_suffix=endpoint, ok_codes=(200, 404), resp_type="response", error_handler=unit42_error_handler, retries=RETRY_COUNT, status_list_to_retry=STATUS_CODES_TO_RETRY, ) def create_dbot_score( indicator: str, indicator_type: str, verdict: str, reliability: str = DBotScoreReliability.A_PLUS_PLUS, ) -> Common.DBotScore: """ Create DBotScore object Args: indicator: The indicator value indicator_type: Type of indicator verdict: Verdict from API reliability: Source reliability Returns: DBotScore object """ score: int = VERDICT_TO_SCORE.get(verdict.lower() or "unknown", Common.DBotScore.NONE) # Add malicious description if the verdict is malicious malicious_description = None if verdict.lower() == "malicious": malicious_description = f"Unit 42 Intelligence classified this {indicator_type.lower()} as malicious" return Common.DBotScore( indicator=indicator, indicator_type=indicator_type, integration_name=INTEGRATION_NAME, score=score, reliability=reliability, malicious_description=malicious_description, ) def remove_mitre_technique_id_prefix(threat_name: str) -> str: """ Remove MITRE technique ID prefix from threat name if present Args: threat_name: The threat name that may contain MITRE technique ID prefix Returns: Threat name with MITRE technique ID prefix removed if applicable Examples: >>> remove_mitre_technique_id_prefix("T1590 - Gather Victim Network Information") "Gather Victim Network Information" >>> remove_mitre_technique_id_prefix("Regular Threat Name") "Regular Threat Name" >>> remove_mitre_technique_id_prefix("T123 - Some Technique") "Some Technique" >>> remove_mitre_technique_id_prefix("Not a MITRE ID - Something") "Not a MITRE ID - Something" """ if " - " in threat_name: parts = threat_name.split(" - ", 1) if len(parts) == 2 and parts[0].startswith("T") and parts[0][1:].isdigit(): return parts[1] return threat_name def create_relationships( indicator: str, indicator_type: str, threat_objects: list[dict[str, Any]], create_relationships: bool, reliability: str = DBotScoreReliability.A_PLUS_PLUS, ) -> list[EntityRelationship]: """ Create relationships between indicator and threat objects Args: indicator: The indicator value indicator_type: Type of indicator threat_objects: List of threat object associations create_relationships: Whether to create relationships reliability: Source reliability Returns: List of EntityRelationship objects or empty list """ relationships: list[EntityRelationship] = [] if not create_relationships or not threat_objects: demisto.debug(f"Skipping create_relationships as {create_relationships} and {threat_objects=}") return relationships for threat_obj in threat_objects: threat_name = threat_obj.get("name", "") threat_class = threat_obj.get("threat_object_class", "").lower() if not threat_name or threat_class not in INDICATOR_TYPE_MAPPING: demisto.debug(f"Skipping create_relationships for threat_name {threat_name} and threat_class {threat_class}") continue # Remove MITRE technique ID prefix for attack patterns if INDICATOR_TYPE_MAPPING[threat_class] == ThreatIntel.ObjectsNames.ATTACK_PATTERN: threat_name = remove_mitre_technique_id_prefix(threat_name) relationship = EntityRelationship( name=EntityRelationship.Relationships.RELATED_TO, entity_a=indicator, entity_a_type=indicator_type, entity_b=threat_name, entity_b_type=INDICATOR_TYPE_MAPPING[threat_class], source_reliability=reliability, brand=INTEGRATION_NAME, ) relationships.append(relationship) return relationships def extract_response_data(response: dict[str, Any]) -> dict[str, Any]: """ Extract data from API response Args: response: API response as dictionary Returns: Dictionary containing extracted data """ return { "indicator_value": response.get("indicator_value", ""), "indicator_type": response.get("indicator_type", ""), "counts": response.get("counts", []), "verdict": response.get("verdict", "unknown"), "verdict_categories": [item.get("value") for item in response.get("verdict_categories", [])], "first_seen": response.get("first_seen", ""), "last_seen": response.get("last_seen", ""), "updated_at": response.get("updated_at", ""), "seen_by": response.get("sources", []), "threat_object_associations": response.get("threat_object_associations", []), "indicator_details": response.get("indicator_details", {}), } def extract_tags_from_threat_objects(threat_objects: list[dict[str, Any]]) -> list[str]: """ Extract tags from threat object associations Args: threat_objects: List of threat object associations Returns: List of tag names """ tags = [] for threat_obj in threat_objects: name = threat_obj.get("name") if name: tags.append(name) # Add aliases as additional tags aliases = threat_obj.get("aliases", []) if aliases: tags.extend([alias for alias in aliases if alias]) return list(set(tags)) # Remove duplicates def extract_malware_families_from_threat_objects(threat_objects: list[dict[str, Any]]) -> str | None: """ Extract malware families from threat object associations Args: threat_objects: List of threat object associations Returns: Malware family name if found, None otherwise """ for threat_obj in threat_objects: threat_class = threat_obj.get("threat_object_class", "").lower() if threat_class == "malware_family": name = threat_obj.get("name") if name: return name return None def build_threat_object_description(threat_obj: dict[str, Any]) -> str: """ Build a comprehensive description for a threat object including highlights, methods, and targets Args: threat_obj: The threat object data Returns: Formatted description string with sections for highlights, methods, and targets """ description = threat_obj.get("description", "").replace("\\n", "\n") # Add highlights section if available highlights = demisto.get(threat_obj, "battlecard_details.highlights", "").replace("\\n", "\n") if highlights and highlights != "Highlights / Key Takeaways (external)": # Do not add if it is only the default title description += "\n\n##" description += highlights # Add methods section if available (for threat actors) methods = demisto.get(threat_obj, "battlecard_details.threat_actor_details.methods", "").replace("\\n", "\n") if methods: description += "\n\n##" description += methods # Add targets section if available (for threat actors) targets = demisto.get(threat_obj, "battlecard_details.threat_actor_details.targets", "").replace("\\n", "\n") if targets: description += "\n\n##" description += targets return description def create_publications(publications_data: list) -> list: """ Creates the publications list of the indicator Args: publications_data: A list of all publications from threat object Returns: A list of publications of the indicator """ publications = [] for data in publications_data: timestamp = data.get("created", "") title = data.get("title", "") url = data.get("url", "") source = data.get("source", INTEGRATION_NAME) publications.append({"link": url, "title": title, "timestamp": timestamp, "source": source}) return publications def create_threat_object_relationships( threat_obj: dict[str, Any], threat_object_name: str, threat_class: str, reliability: str = DBotScoreReliability.A_PLUS_PLUS, ) -> list[EntityRelationship]: """ Create threat object relationships from related_threat_objects Args: threat_obj: The threat object data threat_object_name: Name of the threat object threat_class: The threat object class reliability: Source reliability Returns: List of EntityRelationship objects """ relationships = [] related_threat_objects = threat_obj.get("related_threat_objects", []) for related_obj in related_threat_objects: if not isinstance(related_obj, dict): continue related_name = related_obj.get("name") related_class = related_obj.get("class", "").lower() if related_name and related_class: entity_relationship = EntityRelationship( name=EntityRelationship.Relationships.RELATED_TO, entity_a=threat_object_name, entity_a_type=INDICATOR_TYPE_MAPPING[threat_class], entity_b=related_name, entity_b_type=INDICATOR_TYPE_MAPPING[related_class], source_reliability=reliability, brand=INTEGRATION_NAME, ) relationships.append(entity_relationship.to_entry()) return relationships def create_campaigns_relationships( threat_obj: dict[str, Any], threat_object_name: str, threat_class: str, reliability: str = DBotScoreReliability.A_PLUS_PLUS, ) -> list[EntityRelationship]: """ Create campaigns relationships from campaigns list Args: threat_obj: The threat object data threat_object_name: Name of the threat object threat_class: The threat object class reliability: Source reliability Returns: List of EntityRelationship objects """ relationships = [] campaigns = demisto.get(threat_obj, "battlecard_details.campaigns", []) for campaign in campaigns: if isinstance(campaign, str) and campaign.strip(): entity_relationship = EntityRelationship( name=EntityRelationship.Relationships.RELATED_TO, entity_a=threat_object_name, entity_a_type=INDICATOR_TYPE_MAPPING[threat_class], entity_b=string_to_table_header(campaign), entity_b_type=ThreatIntel.ObjectsNames.CAMPAIGN, source_reliability=reliability, brand=INTEGRATION_NAME, ) relationships.append(entity_relationship.to_entry()) return relationships def create_attack_patterns_relationships( threat_obj: dict[str, Any], threat_actor_name: str, threat_class: str, reliability: str = DBotScoreReliability.A_PLUS_PLUS, ) -> list[EntityRelationship]: """ Create attack patterns relationships from attack patterns associations Args: threat_obj: The threat object data threat_actor_name: Name of the threat actor threat_class: The threat object class reliability: Source reliability Returns: List of EntityRelationship objects """ relationships = [] attack_patterns = demisto.get(threat_obj, "battlecard_details.attack_patterns", []) for pattern in attack_patterns: mitre_id = pattern.get("mitreid", "") pattern_name = pattern.get("name", "") # Skip items with a dot in the mitreid if "." in mitre_id: demisto.debug(f"Skipping attack pattern {pattern_name} with mitreid {mitre_id}") continue if pattern_name and pattern_name.endswith("(enterprise)"): # Remove (enterprise) suffix if present pattern_name = pattern_name.removesuffix("(enterprise)").strip() entity_relationship = EntityRelationship( name=EntityRelationship.Relationships.USES, entity_a=threat_actor_name, entity_a_type=INDICATOR_TYPE_MAPPING[threat_class], entity_b=string_to_table_header(pattern_name), entity_b_type=ThreatIntel.ObjectsNames.ATTACK_PATTERN, source_reliability=reliability, brand=INTEGRATION_NAME, ) relationships.append(entity_relationship.to_entry()) return relationships def create_malware_relationships( threat_obj: dict[str, Any], threat_actor_name: str, threat_class: str, reliability: str = DBotScoreReliability.A_PLUS_PLUS, ) -> list[EntityRelationship]: """ Create malware relationships from malware_associations Args: threat_obj: The threat object data threat_actor_name: Name of the threat actor threat_class: The threat object class reliability: Source reliability Returns: List of EntityRelationship objects """ relationships = [] malware_associations = demisto.get(threat_obj, "battlecard_details.threat_actor_details.malware_associations", []) for relationship in malware_associations: name = relationship.get("name") aliases = relationship.get("aliases", []) if name: # Create a relationship using the name entity_relationship = EntityRelationship( name=EntityRelationship.Relationships.USES, entity_a=threat_actor_name, entity_a_type=INDICATOR_TYPE_MAPPING[threat_class], entity_b=string_to_table_header(name), entity_b_type=ThreatIntel.ObjectsNames.MALWARE, source_reliability=reliability, brand=INTEGRATION_NAME, ) relationships.append(entity_relationship.to_entry()) elif aliases: # Create a relationship for each alias if no name exists for alias in aliases: entity_relationship = EntityRelationship( name=EntityRelationship.Relationships.USES, entity_a=threat_actor_name, entity_a_type=INDICATOR_TYPE_MAPPING[threat_class], entity_b=string_to_table_header(alias), entity_b_type=ThreatIntel.ObjectsNames.MALWARE, source_reliability=reliability, brand=INTEGRATION_NAME, ) relationships.append(entity_relationship.to_entry()) return relationships def create_tools_relationships( threat_obj: dict[str, Any], threat_actor_name: str, threat_class: str, reliability: str = DBotScoreReliability.A_PLUS_PLUS, ) -> list[EntityRelationship]: """ Create tools relationships from tools associations Args: threat_obj: The threat object data threat_actor_name: Name of the threat actor threat_class: The threat object class reliability: Source reliability Returns: List of EntityRelationship objects """ relationships = [] tools_associations = demisto.get(threat_obj, "battlecard_details.threat_actor_details.tools", []) for tool in tools_associations: tool_name = tool.get("name") if tool_name: entity_relationship = EntityRelationship( name=EntityRelationship.Relationships.USES, entity_a=threat_actor_name, entity_a_type=INDICATOR_TYPE_MAPPING[threat_class], entity_b=string_to_table_header(tool_name), entity_b_type=ThreatIntel.ObjectsNames.TOOL, source_reliability=reliability, brand=INTEGRATION_NAME, fields={"tags": f"mitre-id: {tool.get('mitreid')}" if tool.get("mitreid") else ""}, ) relationships.append(entity_relationship.to_entry()) return relationships def create_vulnerabilities_relationships( threat_obj: dict[str, Any], threat_actor_name: str, threat_class: str, reliability: str = DBotScoreReliability.A_PLUS_PLUS, ) -> list[EntityRelationship]: """ Create vulnerabilities relationships from vulnerabilities associations Args: threat_obj: The threat object data threat_actor_name: Name of the threat actor threat_class: The threat object class reliability: Source reliability Returns: List of EntityRelationship objects """ relationships = [] vulnerabilities = demisto.get(threat_obj, "battlecard_details.threat_actor_details.vulnerability_associations", []) for vulnerability in vulnerabilities: cve_id = vulnerability.get("cve") if cve_id: entity_relationship = EntityRelationship( name=EntityRelationship.Relationships.EXPLOITS, entity_a=threat_actor_name, entity_a_type=INDICATOR_TYPE_MAPPING[threat_class], entity_b=cve_id.upper(), entity_b_type=FeedIndicatorType.CVE, source_reliability=reliability, brand=INTEGRATION_NAME, ) relationships.append(entity_relationship.to_entry()) return relationships def create_actor_relationships( threat_obj: dict[str, Any], malware_family_name: str, threat_class: str, reliability: str = DBotScoreReliability.A_PLUS_PLUS, ) -> list[EntityRelationship]: """ Create actor relationships from actor_associations Args: threat_obj: The threat object data malware_family_name: Name of the malware family threat_class: The threat object class reliability: Source reliability Returns: List of EntityRelationship objects """ relationships = [] actor_associations = demisto.get(threat_obj, "battlecard_details.malware_family_details.actor_associations", []) for relationship in actor_associations: aliases = relationship.get("aliases", []) name = relationship.get("name") if aliases: # Create a relationship for each alias for alias in aliases: entity_relationship = EntityRelationship( name=EntityRelationship.Relationships.USED_BY, entity_a=malware_family_name, entity_a_type=INDICATOR_TYPE_MAPPING[threat_class], entity_b=string_to_table_header(alias), entity_b_type=ThreatIntel.ObjectsNames.THREAT_ACTOR, source_reliability=reliability, brand=INTEGRATION_NAME, ) relationships.append(entity_relationship.to_entry()) elif name: # Create a relationship using the name if no aliases exist entity_relationship = EntityRelationship( name=EntityRelationship.Relationships.USED_BY, entity_a=malware_family_name, entity_a_type=INDICATOR_TYPE_MAPPING[threat_class], entity_b=string_to_table_header(name), entity_b_type=ThreatIntel.ObjectsNames.THREAT_ACTOR, source_reliability=reliability, brand=INTEGRATION_NAME, ) relationships.append(entity_relationship.to_entry()) return relationships def create_location_indicators_and_relationships( threat_obj: dict[str, Any], threat_actor_name: str, reliability: str = DBotScoreReliability.A_PLUS_PLUS, ) -> list[dict[str, Any]]: """ Create location indicators from affected regions and origin field and build relationships Args: threat_obj: The threat object data threat_actor_name: Name of the threat actor to create relationships with reliability: Source reliability Returns: List of location indicators with relationships """ location_indicators: list = [] # Handle affected regions affected_regions = demisto.get(threat_obj, "battlecard_details.threat_actor_details.affected_regions", []) # in case affected_regions is "null", return empty list. if not isinstance(affected_regions, list): return location_indicators for region in affected_regions: if isinstance(region, str) and region.strip(): region_lower = region.strip().lower() # Use the standardized region name if it matches our enum standardized_region = VALID_REGIONS.get(region_lower) if not standardized_region: demisto.debug(f"Skipping region {region} as it is not in the valid regions enum") continue # Create EntityRelationship for the location entity_relationship = EntityRelationship( name=EntityRelationship.Relationships.TARGETS, entity_a=threat_actor_name, entity_a_type=ThreatIntel.ObjectsNames.THREAT_ACTOR, entity_b=standardized_region, entity_b_type=FeedIndicatorType.Location, source_reliability=reliability, brand=INTEGRATION_NAME, ) location_indicator = { "value": standardized_region, "type": FeedIndicatorType.Location, "score": Common.DBotScore.NONE, "service": INTEGRATION_NAME, "relationships": [entity_relationship.to_entry()], "fields": { "geocountry": standardized_region, }, } location_indicators.append(location_indicator) return location_indicators def get_threat_object_score(threat_class: str) -> int: """ Get the appropriate score for a threat object based on its class Args: threat_class: The threat object class (lowercase) Returns: Appropriate ThreatIntel score or Common.DBotScore.NONE as default """ if threat_class not in INDICATOR_TYPE_MAPPING: return Common.DBotScore.NONE threat_type = INDICATOR_TYPE_MAPPING[threat_class] if threat_type == ThreatIntel.ObjectsNames.MALWARE: return ThreatIntel.ObjectsScore.MALWARE elif threat_type == ThreatIntel.ObjectsNames.THREAT_ACTOR: return ThreatIntel.ObjectsScore.THREAT_ACTOR elif threat_type == ThreatIntel.ObjectsNames.CAMPAIGN: return ThreatIntel.ObjectsScore.CAMPAIGN elif threat_type == ThreatIntel.ObjectsNames.ATTACK_PATTERN: return ThreatIntel.ObjectsScore.ATTACK_PATTERN return Common.DBotScore.NONE def create_threat_object_indicators( threat_objects: list[dict[str, Any]], reliability: str = "A++ - Reputation script" ) -> list[dict[str, Any]]: """ Create threat object indicators from threat object associations Args: threat_objects: List of threat object associations reliability: Source reliability Returns: List of threat object indicators """ indicators = [] for threat_obj in threat_objects: name = threat_obj.get("name") threat_class = threat_obj.get("threat_object_class", "").lower() if not name or threat_class not in INDICATOR_TYPE_MAPPING: continue # Create relationships relationships = [] relationships += create_threat_object_relationships(threat_obj, name, threat_class, reliability) relationships += create_campaigns_relationships(threat_obj, name, threat_class, reliability) relationships += create_attack_patterns_relationships(threat_obj, name, threat_class, reliability) relationships += create_malware_relationships(threat_obj, name, threat_class, reliability) relationships += create_tools_relationships(threat_obj, name, threat_class, reliability) relationships += create_vulnerabilities_relationships(threat_obj, name, threat_class, reliability) relationships += create_actor_relationships(threat_obj, name, threat_class, reliability) # Create fields with threat object details fields = { "description": build_threat_object_description(threat_obj), "reportedby": threat_obj.get("sources"), "aliases": [string_to_table_header(alias) for alias in threat_obj.get("aliases", [])], "industrysectors": [ string_to_table_header(industry) for industry in demisto.get(threat_obj, "battlecard_details.industries", []) ], "primarymotivation": string_to_table_header( demisto.get(threat_obj, "battlecard_details.threat_actor_details.primary_motivation", "") ), "publications": create_publications(threat_obj.get("publications", [])), "geocountry": demisto.get(threat_obj, "battlecard_details.threat_actor_details.origin", "").upper(), "ismalwarefamily": "True" if threat_class == "malware_family" else "False", } indicator_data = { "value": name, "type": INDICATOR_TYPE_MAPPING[threat_class], "score": get_threat_object_score(threat_class), "service": INTEGRATION_NAME, "relationships": relationships, "fields": fields, "rawJSON": threat_obj, } indicators.append(indicator_data) # Create location indicators from affected regions location_indicators = create_location_indicators_and_relationships(threat_obj, name, reliability) indicators.extend(location_indicators) return indicators def create_context_data(response_data: dict[str, Any], indicator_value: str | None = None) -> dict[str, Any]: """ Create context data for indicators Args: response_data: Extracted response data Returns: Dictionary containing context data """ return { "Value": indicator_value or response_data["indicator_value"], "Type": INDICATOR_TYPE_MAPPING.get(response_data["indicator_type"]), "Verdict": string_to_table_header(response_data["verdict"]), "VerdictCategories": list({string_to_table_header(item) for item in response_data["verdict_categories"]}), "Counts": response_data["counts"], "FirstSeen": response_data["first_seen"], "LastSeen": response_data["last_seen"], "SeenBy": list({string_to_table_header(item) for item in response_data["seen_by"]}), "EnrichedThreatObjectAssociation": response_data["threat_object_associations"], } def construct_404_response(indicator_value: str, indicator_type: str) -> dict[str, Any]: """ Construct a 404 response for a missing indicator Args: indicator_value: Value of the indicator indicator_type: Type of indicator Returns: Dictionary containing indicator default response in cases of 404 """ return { "indicator_value": indicator_value, "indicator_type": indicator_type, "verdict": "Unknown", "verdict_categories": [], "counts": [{"count_type": "wf_sample", "count_values": {"benign": 0, "grayware": 0, "malware": 0}}], "first_seen": "", "last_seen": "", "seen_by": [], "threat_object_associations": [], "is_observed": False, "sources": [], } #### TEST MODULE #### def test_module(client: Client) -> str: """ Test the integration by making a simple API call Args: client: Unit 42 Intelligence client Returns: 'ok' if test passed, error message otherwise """ try: # Test with a known safe domain client.lookup_indicator("domain", "example.com") return "ok" except Exception as e: return f"Test failed: {str(e)}" #### COMMAND FUNCTIONS #### def ip_command(client: Client, args: dict[str, Any]) -> CommandResults: """ Enrich IP address with Unit 42 Intelligence Args: client: Unit 42 Intelligence client args: Command arguments Returns: CommandResults object """ ip = args.get("ip", "") create_relationships_flag = argToBoolean(args.get("create_relationships", True)) create_threat_object_indicators_flag = argToBoolean(args.get("create_threat_object_indicators", False)) response = client.lookup_indicator("ip", ip) if response.status_code == 404: response_data = construct_404_response(ip, "IP") else: response_data = extract_response_data(response.json()) threat_objects = response_data["threat_object_associations"] # Create DBotScore dbot_score = create_dbot_score(ip, DBotScoreType.IP, response_data["verdict"], client.reliability) # Extract tags and malware families from threat objects tags = extract_tags_from_threat_objects(threat_objects) malware_families = extract_malware_families_from_threat_objects(threat_objects) # Create enriched IP indicator with tags and malware families ip_indicator = Common.IP(ip=ip, dbot_score=dbot_score, tags=tags, malware_family=malware_families) # Create relationships relationships = create_relationships(ip, FeedIndicatorType.IP, threat_objects, create_relationships_flag, client.reliability) # Create indicators from relationships if create_threat_object_indicators_flag: threat_indicators = create_threat_object_indicators(threat_objects, client.reliability) if threat_indicators: demisto.createIndicators(threat_indicators) # Create context data context_data = create_context_data(response_data) readable_output = tableToMarkdown( f"Unit 42 Intelligence results for IP: {ip}", context_data, headers=["Value", "Verdict", "VerdictCategories", "SeenBy", "FirstSeen", "LastSeen"], headerTransform=pascalToSpace, removeNull=True, ) return CommandResults( outputs_prefix="Unit42Intelligence.IP", outputs_key_field="Value", outputs=context_data, readable_output=readable_output, indicator=ip_indicator, relationships=relationships, ) def domain_command(client: Client, args: dict[str, Any]) -> CommandResults: """ Enrich domain with Unit 42 Intelligence Args: client: Unit 42 Intelligence client args: Command arguments Returns: CommandResults object """ domain = args.get("domain", "") create_relationships_flag = argToBoolean(args.get("create_relationships", True)) create_threat_object_indicators_flag = argToBoolean(args.get("create_threat_object_indicators", False)) response = client.lookup_indicator("domain", domain) if response.status_code == 404: response_data = construct_404_response(domain, "Domain") else: response_data = extract_response_data(response.json()) threat_objects = response_data["threat_object_associations"] # Create DBotScore dbot_score = create_dbot_score(domain, DBotScoreType.DOMAIN, response_data["verdict"], client.reliability) # Extract tags and malware families from threat objects tags = extract_tags_from_threat_objects(threat_objects) malware_families = extract_malware_families_from_threat_objects(threat_objects) # Create enriched Domain indicator with tags and malware families domain_indicator = Common.Domain(domain=domain, dbot_score=dbot_score, tags=tags, malware_family=malware_families) # Create relationships relationships = create_relationships( domain, FeedIndicatorType.Domain, threat_objects, create_relationships_flag, client.reliability ) # Create indicators from relationships if create_threat_object_indicators_flag: threat_indicators = create_threat_object_indicators(threat_objects, client.reliability) if threat_indicators: demisto.createIndicators(threat_indicators) # Create context data context_data = create_context_data(response_data) readable_output = tableToMarkdown( f"Unit 42 Intelligence results for Domain: {domain}", context_data, headers=["Value", "Verdict", "VerdictCategories", "SeenBy", "FirstSeen", "LastSeen"], headerTransform=pascalToSpace, removeNull=True, ) return CommandResults( outputs_prefix="Unit42Intelligence.Domain", outputs_key_field="Value", outputs=context_data, readable_output=readable_output, indicator=domain_indicator, relationships=relationships, ) def url_command(client: Client, args: dict[str, Any]) -> CommandResults: """ Enrich URL with Unit 42 Intelligence Args: client: Unit 42 Intelligence client args: Command arguments Returns: CommandResults object """ url = args.get("url", "") create_relationships_flag = argToBoolean(args.get("create_relationships", True)) create_threat_object_indicators_flag = argToBoolean(args.get("create_threat_object_indicators", False)) response = client.lookup_indicator("url", url) if response.status_code == 404: response_data = construct_404_response(url, "URL") else: response_data = extract_response_data(response.json()) threat_objects = response_data["threat_object_associations"] # Create DBotScore dbot_score = create_dbot_score(url, DBotScoreType.URL, response_data["verdict"], client.reliability) # Extract tags and malware families from threat objects tags = extract_tags_from_threat_objects(threat_objects) malware_families = extract_malware_families_from_threat_objects(threat_objects) # Create enriched URL indicator with tags and malware families url_indicator = Common.URL(url=url, dbot_score=dbot_score, tags=tags, malware_family=malware_families) # Create relationships relationships = create_relationships( url, FeedIndicatorType.URL, threat_objects, create_relationships_flag, client.reliability ) # Create indicators from relationships if create_threat_object_indicators_flag: threat_indicators = create_threat_object_indicators(threat_objects, client.reliability) if threat_indicators: demisto.createIndicators(threat_indicators) # Create context data, Use the original URL in the indicator value since the returned URL is encoded. context_data = create_context_data(response_data, indicator_value=url) readable_output = tableToMarkdown( f"Unit 42 Intelligence results for URL: {url}", context_data, headers=["Value", "Verdict", "VerdictCategories", "SeenBy", "FirstSeen", "LastSeen"], headerTransform=pascalToSpace, removeNull=True, ) return CommandResults( outputs_prefix="Unit42Intelligence.URL", outputs_key_field="Value", outputs=context_data, readable_output=readable_output, indicator=url_indicator, relationships=relationships, ) def file_command(client: Client, args: dict[str, Any]) -> CommandResults: """ Enrich file hash with Unit 42 Intelligence Args: client: Unit 42 Intelligence client args: Command arguments Returns: CommandResults object """ file_hash = args.get("file", "") create_relationships_flag = argToBoolean(args.get("create_relationships", True)) create_threat_object_indicators_flag = argToBoolean(args.get("create_threat_object_indicators", False)) # Validate hash type - Unit 42 Intelligence only supports SHA256 hash_type = get_hash_type(file_hash) if hash_type != "sha256": return CommandResults( readable_output=f"Unit 42 Intelligence only supports SHA256 hashes. Provided hash type: {hash_type}" ) response = client.lookup_indicator("filehash_sha256", file_hash) if response.status_code == 404: response_data = construct_404_response(file_hash, "File") else: response_data = extract_response_data(response.json()) threat_objects = response_data["threat_object_associations"] # Create DBotScore dbot_score = create_dbot_score(file_hash, DBotScoreType.FILE, response_data["verdict"], client.reliability) # Extract tags and malware families from threat objects tags = extract_tags_from_threat_objects(threat_objects) malware_families = extract_malware_families_from_threat_objects(threat_objects) # Create enriched File indicator with proper hash field assignment file_indicator = Common.File( size=demisto.get(response_data, "indicator_details.file_size", ""), file_type=demisto.get(response_data, "indicator_details.file_type", ""), imphash=demisto.get(response_data, "indicator_details.file_hashes.imphash", ""), md5=demisto.get(response_data, "indicator_details.file_hashes.md5", ""), sha1=demisto.get(response_data, "indicator_details.file_hashes.sha1", ""), sha256=file_hash, ssdeep=demisto.get(response_data, "indicator_details.file_hashes.ssdeep", ""), dbot_score=dbot_score, tags=tags, malware_family=malware_families, ) # Create relationships relationships = create_relationships( file_hash, FeedIndicatorType.File, threat_objects, create_relationships_flag, client.reliability ) # Create indicators from relationships if create_threat_object_indicators_flag: threat_indicators = create_threat_object_indicators(threat_objects, client.reliability) if threat_indicators: demisto.createIndicators(threat_indicators) # Create context data context_data = create_context_data(response_data) readable_output = tableToMarkdown( f"Unit 42 Intelligence results for File: {file_hash}", context_data, headers=["Value", "Verdict", "VerdictCategories", "SeenBy", "FirstSeen", "LastSeen"], headerTransform=pascalToSpace, removeNull=True, ) return CommandResults( outputs_prefix="Unit42Intelligence.File", outputs_key_field="Value", outputs=context_data, readable_output=readable_output, indicator=file_indicator, relationships=relationships, ) #### MAIN FUNCTION #### def main() -> None: """Main function, parses params and runs command functions""" global _UCP_AUTH_PARAMS_INJECTED _UCP_AUTH_PARAMS_INJECTED = True # type: ignore[name-defined] # noqa: F821 params = demisto.params() args = demisto.args() command = demisto.command() # Get parameters verify_certificate = not argToBoolean(params.get("insecure", False)) proxy = argToBoolean(params.get("proxy", False)) reliability = params.get("integration_reliability", "A++ - Reputation script") create_relationships = argToBoolean(params.get("create_relationships", True)) create_threat_object_indicators = argToBoolean(params.get("create_threat_object_indicators", False)) # Add create_relationships to args for commands args["create_relationships"] = create_relationships args["create_threat_object_indicators"] = create_threat_object_indicators demisto.debug(f"Command being called is {command}") try: client = Client( verify=verify_certificate, proxy=proxy, reliability=reliability, ) if command == "test-module": result = test_module(client) return_results(result) elif command == "ip": results = [] ips = argToList(args.get("ip", "")) for ip in ips: args["ip"] = ip results.append(ip_command(client, args)) return_results(results) elif command == "domain": results = [] domains = argToList(args.get("domain", "")) for domain in domains: args["domain"] = domain results.append(domain_command(client, args)) return_results(results) elif command == "url": results = [] # Use smart URL parsing to handle URLs with commas in parameters urls = parse_url_list(args.get("url", "")) for url in urls: args["url"] = url results.append(url_command(client, args)) return_results(results) elif command == "file": results = [] files = argToList(args.get("file", "")) for file in files: args["file"] = file results.append(file_command(client, args)) return_results(results) else: raise NotImplementedError(f"Command {command} is not implemented") except Exception as e: demisto.error(traceback.format_exc()) return_error(f"Failed to execute {command} command.\nError:\n{str(e)}") if __name__ in ("__main__", "__builtin__", "builtins"): main()