BrandefenseDRPS
Brandefense is a Cyber Intelligence Platform that responds directly and effectively to today's complex cyber threats.
Data Enrichment & Threat Intelligence · Brandefense Digital Risk Protection Services
Details
| ID | BrandefenseDRPS |
|---|---|
| Provider | Brandefense |
| Category | Data Enrichment & Threat Intelligence |
| From Version | 6.10.0 |
| Docker Image | demisto/python3:3.12.13.10404775 |
README
Brandefense Digital Risk Protection Services
Brandefense is a SaaS platform offering Digital Risk Protection Services (DRPS), External Attack Surface Management (EASM), and Actionable Threat Intelligence. This integration connects Cortex XSOAR with the Brandefense platform to automate threat intelligence, brand monitoring, and phishing response operations.
What does this pack do?
- Fetches incidents and intelligence reports from Brandefense as Cortex XSOAR incidents (with deduplication).
- Investigates IP addresses, domains, URLs, and file hashes against Brandefense IoC data.
- Manages incidents: view details, indicators, related incidents, and change status.
- Retrieves intelligence reports with indicators and rules.
- Runs CTI-powered threat searches.
- Lists and searches monitored assets.
- Detects and investigates compromised devices.
- Reviews the Brandefense platform audit trail.
- Retrieves domain risk assessments for third-party risk management.
- Creates confirmed phishing incidents and requests takedowns.
- Retrieves consolidated indicators by type (leak, phishing, credit card, CVE, and similar categories).
Fetch Incidents
The integration supports automatic incident fetching without duplicates.
- Fetches both Incidents and Intelligence reports (configurable via Fetching Issue Types).
- Tracks previously seen incident codes across fetch cycles to prevent duplicates.
- Uses timestamp and code-based deduplication.
- Auto-classifies items into the
Brandefense IncidentandBrandefense Intelligencetypes via the built-in classifier and incoming mapper.
Configure Brandefense Digital Risk Protection Services on Cortex XSOAR
| Parameter | Description | Required |
|---|---|---|
| Server URL (e.g. https://api.brandefense.io) | True | |
| API Key | You can reach out your access token: https://brandefense.io/ | True |
| Trust any certificate (not secure) | False | |
| Use system proxy settings | False | |
| Fetch incidents | False | |
| Incidents Fetch Interval | Start fetching incidents from the specified time. | False |
| First time fetching | False | |
| Incident type | False | |
| Max Results | False | |
| Maximum incidents per fetch | Maximum number of incidents to fetch per cycle. | False |
| Source Reliability | Reliability of the source providing the intelligence data. | False |
| Incident Category | This parameter will request and show the incident’s related module category. | False |
| Incident Module | This parameter will request and show the incident’s related module. | False |
| Incident Status | This parameter will request incident’s by status. | False |
| Intelligence Category | This parameter allows you to filter intelligence values by categories. | False |
| Intelligence Search | This parameter allows you to filter intelligence values with keyword search. | False |
| Fetching Issue Types | This parameter allows you to filter fetching by issue type. | True |
| Incident Rules | This parameter will request and show the incident’s related template. Don’t select any to get all alerts. | 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
Investigate an IP address against Brandefense threat intelligence.
Base Command
ip
Input
| Argument Name | Description | Required |
|---|---|---|
| ip | IP address to investigate. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| IP.Address | String | The IP address. |
| IP.Malicious.Vendor | String | 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. |
| Brandefense.IP.data | String | The IP address value. |
| Brandefense.IP.severity | String | Severity level. |
| Brandefense.IP.category | String | Category of the threat. |
| Brandefense.IP.first_seen | Date | First seen date. |
| Brandefense.IP.last_seen | Date | Last seen date. |
domain
Investigate a domain against Brandefense threat intelligence.
Base Command
domain
Input
| Argument Name | Description | Required |
|---|---|---|
| domain | Domain name to investigate. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| Domain.Name | String | The domain name. |
| Domain.Malicious.Vendor | String | 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. |
| Brandefense.Domain.data | String | The domain value. |
| Brandefense.Domain.severity | String | Severity level. |
| Brandefense.Domain.category | String | Category of the threat. |
| Brandefense.Domain.first_seen | Date | First seen date. |
| Brandefense.Domain.last_seen | Date | Last seen date. |
url
Investigate a URL against Brandefense threat intelligence.
Base Command
url
Input
| Argument Name | Description | Required |
|---|---|---|
| url | URL address to investigate. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| URL.Data | String | The URL. |
| URL.Malicious.Vendor | String | 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. |
| Brandefense.URL.data | String | The URL value. |
| Brandefense.URL.severity | String | Severity level. |
| Brandefense.URL.category | String | Category of the threat. |
| Brandefense.URL.first_seen | Date | First seen date. |
| Brandefense.URL.last_seen | Date | Last seen date. |
file
Investigate a file hash against Brandefense threat intelligence.
Base Command
file
Input
| Argument Name | Description | Required |
|---|---|---|
| file | File hash to investigate (MD5, SHA1, or 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 | 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. |
| Brandefense.File.data | String | The hash value. |
| Brandefense.File.severity | String | Severity level. |
| Brandefense.File.category | String | Category of the threat. |
| Brandefense.File.first_seen | Date | First seen date. |
| Brandefense.File.last_seen | Date | Last seen date. |
brandefense_get_incidents
Get Brandefense incidents with optional filtering by status, module, category, and time period.
Base Command
brandefense_get_incidents
Input
| Argument Name | Description | Required |
|---|---|---|
| status | Filter incidents by status. Possible values are: OPEN, IN_PROGRESS, CLOSED, RISK_ACCEPTED, REJECTED. Default is OPEN. | Optional |
| time_range | Predefined time range. Overrides ‘period’ when set. Select ‘Custom’ to use created_at_range. Possible values are: Last 24 Hours, Last 7 Days, Last 30 Days, Last 90 Days, Last 6 Months, Last 1 Year, Custom. | Optional |
| created_at_range | Custom date range (start,end). Example: 2020-10-10,2023-10-10. Used when time_range is ‘Custom’ or not set. | Optional |
| period | Fetch period in hours. Used as fallback when time_range is not set. Default is 1. | Optional |
| module | Filter by incident module. Possible values are: SENSITIVE_FILE_DISCLOSURE, BREACH_MONITORING, PHISHING_MONITORING, DARKWEB_INTELLIGENCE, SOCIAL_MEDIA_MONITORING, MALICIOUS_FILES, EXECUTIVE_PROTECTION, SUPPLY_CHAIN_SECURITY, VULNERABILITY_MANAGEMENT, ATTACK_SURFACE, VULNERABILITY_INTELLIGENCE, INTELLIGENCE, THREAT_INTELLIGENCE, CREDIT_CARD, FRAUD_PROTECTION, CUSTOM_INVESTIGATION, MALWARE_ANALYZE, INVESTIGATION. | Optional |
| module_category | Filter by module category. Possible values are: BRAND_MONITORING, EXECUTIVE_PROTECTION, SUPPLY_CHAIN_SECURITY, EXPOSURE_MANAGEMENT, INTELLIGENCE, FRAUD_MONITORING, INTELLIGENCE_SUPPORT, INVESTIGATION. | Optional |
| MaxResults | Maximum number of incidents to return. Default is 100. | Optional |
| search | Search keywords within incident title or code. | Optional |
| severity | Filter by incident severity. Possible values are: INFO, LOW, MEDIUM, HIGH, CRITICAL. | Optional |
| tags | Filter by tags (comma-separated). | Optional |
| network_type | Filter by network type. Possible values are: DARK_WEB, SURFACE_WEB. | Optional |
| mitre_tactics | Filter by MITRE ATT&CK tactics. Possible values are: RECONNAISSANCE, RESOURCE_DEVELOPMENT, INITIAL_ACCESS, EXECUTION, PERSISTENCE, PRIVILEGE_ESCALATION, DEFENSE_EVASION, CREDENTIAL_ACCESS, DISCOVERY, LATERAL_MOVEMENT, COLLECTION, COMMAND_AND_CONTROL, EXFILTRATION, IMPACT. | Optional |
| ordering | Order results. Possible values are: created_at, -created_at, severity, -severity. | Optional |
| has_indicator | Filter incidents that have indicators. Possible values are: true, false. | Optional |
| has_attachment | Filter incidents that have attachments. Possible values are: true, false. | Optional |
| type | Filter by incident type. Possible values are: COMPROMISED_EMPLOYEE_ACCOUNT, COMPROMISED_CLIENT_ACCOUNT, EXECUTIVE_PERSON_EMAIL_LEAK, COMPROMISED_DEVICE, CONFIRMED_PHISHING_ADDRESS, POTENTIAL_PHISHING_ADDRESS, DARKWEB_INTELLIGENCE, SENSITIVE_FILE_DISCLOSURE, CONFIRMED_IMPERSONATED_ACCOUNT, POTENTIAL_IMPERSONATED_ACCOUNT, VULNERABLE_TECHNOLOGY_ASSESSMENT, CREDIT_CARD, ATTACK_SURFACE, CUSTOM_INVESTIGATION, MALWARE_ANALYZE, FRAUD_PROTECTION, VULNERABILITY_DETECTION, OTHER. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Brandefense.Incident.id | Number | Incident ID. |
| Brandefense.Incident.code | String | Incident code. |
| Brandefense.Incident.title | String | Incident title. |
| Brandefense.Incident.created_at | Date | Incident creation date. |
| Brandefense.Incident.status | String | Incident status. |
| Brandefense.Incident.severity | String | Incident severity. |
| Brandefense.Incident.reference_url | String | URL to view the incident in Brandefense. |
| Brandefense.Incident.indicators | Unknown | List of indicators associated with the incident. |
brandefense_get_incident_detail
Get detailed information for a specific Brandefense incident.
Base Command
brandefense_get_incident_detail
Input
| Argument Name | Description | Required |
|---|---|---|
| code | Incident code identifier. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| Brandefense.IncidentDetail.code | String | Incident code. |
| Brandefense.IncidentDetail.title | String | Incident title. |
| Brandefense.IncidentDetail.description | String | Incident description. |
| Brandefense.IncidentDetail.severity | String | Incident severity. |
| Brandefense.IncidentDetail.status | String | Incident status. |
| Brandefense.IncidentDetail.created_at | Date | Creation date. |
| Brandefense.IncidentDetail.reference_url | String | URL to view in Brandefense. |
brandefense_change_incident_status
Change the status of a Brandefense incident.
Base Command
brandefense_change_incident_status
Input
| Argument Name | Description | Required |
|---|---|---|
| code | Incident code identifier. | Required |
| status | New incident status. Possible values are: OPEN, IN_PROGRESS, CLOSED, RISK_ACCEPTED, REJECTED. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| Brandefense.ChangingStatus.code | String | Incident code. |
| Brandefense.ChangingStatus.status | String | Updated incident status. |
brandefense_incident_indicators
Get indicators associated with a Brandefense incident.
Base Command
brandefense_incident_indicators
Input
| Argument Name | Description | Required |
|---|---|---|
| code | Incident code identifier. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| Brandefense.Incident.Indicators | Unknown | List of indicators for the incident. |
brandefense_get_incident_relatives
Get related incidents for a specific Brandefense incident.
Base Command
brandefense_get_incident_relatives
Input
| Argument Name | Description | Required |
|---|---|---|
| code | Incident code identifier. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| Brandefense.Incident.Relatives | Unknown | List of related incidents. |
threat_search
Perform a CTI threat search and poll for results using ScheduledCommand.
Base Command
threat_search
Input
| Argument Name | Description | Required |
|---|---|---|
| value | Value to search for (domain, IP, hash, etc.). | Required |
| interval_in_seconds | Polling interval in seconds between checks. Default is 20. | Optional |
| timeout_in_seconds | Maximum time in seconds to wait for results before timing out. Default is 600. | Optional |
| uuid | Threat search UUID for continuing an in-progress search (internal). | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Brandefense.ThreatSearch.uuid | String | Threat search UUID. |
| Brandefense.ThreatSearch.result | Unknown | Threat search result data. |
brandefense_get_intelligences
Get Brandefense intelligence reports with optional filtering.
Base Command
brandefense_get_intelligences
Input
| Argument Name | Description | Required |
|---|---|---|
| category | Filter by intelligence category. Possible values are: STRATEGIC_INTELLIGENCE, FRAUD_INTELLIGENCE, TACTICAL_INTELLIGENCE, OPERATIONAL_INTELLIGENCE, SECURITY_NEWS, THREAT_REPORTS. | Optional |
| time_range | Predefined time range. Overrides ‘period’ when set. Select ‘Custom’ to use created_at_range. Possible values are: Last 24 Hours, Last 7 Days, Last 30 Days, Last 90 Days, Last 6 Months, Last 1 Year, Custom. | Optional |
| created_at_range | Custom date range (start,end). Example: 2020-10-10,2023-10-10. Used when time_range is ‘Custom’ or not set. | Optional |
| period | Fetch period in hours. Used as fallback when time_range is not set. Default is 24. | Optional |
| search | Keyword to filter intelligence by tag search. | Optional |
| MaxResults | Maximum number of intelligence reports to return. Default is 100. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Brandefense.Intelligence.code | String | Intelligence code. |
| Brandefense.Intelligence.title | String | Intelligence title. |
| Brandefense.Intelligence.severity | String | Intelligence severity. |
| Brandefense.Intelligence.created_at | Date | Creation date. |
| Brandefense.Intelligence.reference_url | String | URL to view in Brandefense. |
brandefense_get_intelligence_detail
Get detailed information for a specific intelligence report.
Base Command
brandefense_get_intelligence_detail
Input
| Argument Name | Description | Required |
|---|---|---|
| code | Intelligence code identifier. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| Brandefense.IntelligenceDetail.code | String | Intelligence code. |
| Brandefense.IntelligenceDetail.title | String | Intelligence title. |
| Brandefense.IntelligenceDetail.description | String | Intelligence description. |
| Brandefense.IntelligenceDetail.severity | String | Intelligence severity. |
| Brandefense.IntelligenceDetail.created_at | Date | Creation date. |
| Brandefense.IntelligenceDetail.reference_url | String | URL to view in Brandefense. |
brandefense_intelligence_indicators
Get indicators associated with a Brandefense intelligence report.
Base Command
brandefense_intelligence_indicators
Input
| Argument Name | Description | Required |
|---|---|---|
| code | Intelligence code identifier. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| Brandefense.Intelligence.Indicators | Unknown | List of indicators for the intelligence report. |
brandefense_get_intelligence_rules
Get rules associated with a Brandefense intelligence report.
Base Command
brandefense_get_intelligence_rules
Input
| Argument Name | Description | Required |
|---|---|---|
| code | Intelligence code identifier. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| Brandefense.Intelligence.Rules | Unknown | Rules associated with the intelligence report. |
brandefense_get_assets
Get list of monitored assets from Brandefense.
Base Command
brandefense_get_assets
Input
| Argument Name | Description | Required |
|---|---|---|
| type | Filter by asset type. Possible values are: DOMAIN, KEYWORD, URL, IP_ADDRESS, CIDR, EXECUTIVE_NAME, EXECUTIVE_EMAIL, EXECUTIVE_ACCOUNT, EXECUTIVE_NICKNAME, BIN_NUMBER, PRODUCT, GIT_REPO, GIT_ACCOUNT, PHISHING_RULE, LOGIN_PAGES, OFFICIAL_SOCIAL_MEDIA_ACCOUNTS, OFFICIAL_MOBILE_APPS, ADMIN_PAGES. | Optional |
| severity | Filter by severity. Possible values are: HIGH, MEDIUM, LOW. | Optional |
| status | Filter by status. Possible values are: ACTIVE, SUGGESTED, REJECTED, PASSIVE. | Optional |
| search | Keyword search. | Optional |
| module | Filter by module code. | Optional |
| max_results | Maximum number of assets to return. Default is 50. | Optional |
| ordering | Order results (e.g. -severity, -type, severity, type). | Optional |
| time_range | Predefined time range. Select ‘Custom’ to use created_at_range. Possible values are: Last 24 Hours, Last 7 Days, Last 30 Days, Last 90 Days, Last 6 Months, Last 1 Year, Custom. | Optional |
| created_at_range | Custom date range (start,end). Example: 2020-10-10,2023-10-10. Used when time_range is ‘Custom’ or not set. | Optional |
| threat_type | Filter by threat type. | Optional |
| asset_ilike | Filter assets containing the given keyword. | Optional |
| organization | Filter by organization code (comma-separated for multiple). | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Brandefense.Asset.id | Number | Asset ID. |
| Brandefense.Asset.asset | String | Asset value. |
| Brandefense.Asset.type | String | Asset type. |
| Brandefense.Asset.severity | String | Asset severity. |
| Brandefense.Asset.status | String | Asset status. |
brandefense_get_iocs
Get Indicators of Compromise from Brandefense threat intelligence feeds.
Base Command
brandefense_get_iocs
Input
| Argument Name | Description | Required |
|---|---|---|
| ioc_type | Type of IoC to retrieve. Possible values are: ip_address, domain, url, hash. | Required |
| period | Time period for IoCs (e.g., 24h, 7d). Default is 24h. | Optional |
| exclude_country | Exclude IoCs from specific countries (comma-separated country codes). | Optional |
| include_country | Include IoCs only from specific countries (comma-separated country codes). | Optional |
| module | Filter IoCs by module. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Brandefense.IOC.data | String | IoC value. |
| Brandefense.IOC.type | String | IoC type. |
| Brandefense.IOC.severity | String | IoC severity. |
| Brandefense.IOC.first_seen | Date | First seen date. |
| Brandefense.IOC.last_seen | Date | Last seen date. |
brandefense_get_ioc_list
Fetch and consolidate all IoCs from the last N days (default 30). Pulls all IoC types and merges into a single list.
Base Command
brandefense_get_ioc_list
Input
| Argument Name | Description | Required |
|---|---|---|
| days | Number of days to look back (default 30, max 90). Default is 30. | Optional |
| ioc_type | Comma-separated list of IoC types to fetch. Leave empty for all types. | Optional |
| limit | Maximum total number of IoCs to return. Default is 5000. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Brandefense.IOCList.data | String | IoC value. |
| Brandefense.IOCList.ioc_type | String | IoC type. |
| Brandefense.IOCList.ioc_type_display | String | Human-readable IoC type. |
| Brandefense.IOCList.severity | String | IoC severity. |
| Brandefense.IOCList.first_seen | Date | First seen date. |
| Brandefense.IOCList.last_seen | Date | Last seen date. |
brandefense_get_compromised_devices
Get compromised devices detected by Brandefense.
Base Command
brandefense_get_compromised_devices
Input
| Argument Name | Description | Required |
|---|---|---|
| botnet_id | Specific botnet/device ID to retrieve details for. Leave empty to list all. | Optional |
| username | Filter by username (contains match). | Optional |
| time_range | Predefined time range for detection date. Select ‘Custom’ to use detection_date_range. Possible values are: Last 24 Hours, Last 7 Days, Last 30 Days, Last 90 Days, Last 6 Months, Last 1 Year, Custom. | Optional |
| detection_date_range | Custom date range (start,end). Example: 2020-10-10,2023-10-11. Used when time_range is ‘Custom’ or not set. | Optional |
| search | Search keyword to filter results. | Optional |
| ordering | Order results. Valid values are detection_date, -detection_date. | Optional |
| max_results | Maximum number of devices to return. Default is 10. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Brandefense.CompromisedDevice.id | Number | Device ID. |
| Brandefense.CompromisedDevice | Unknown | Compromised device data. |
brandefense_get_audit_logs
Get audit log entries from Brandefense.
Base Command
brandefense_get_audit_logs
Input
| Argument Name | Description | Required |
|---|---|---|
| type | Filter by audit log type. | Optional |
| search | Search keyword. | Optional |
| time_range | Predefined time range. Select ‘Custom’ to use created_at_range. Possible values are: Last 24 Hours, Last 7 Days, Last 30 Days, Last 90 Days, Last 6 Months, Last 1 Year, Custom. | Optional |
| created_at_range | Custom date range (start,end). Example: 2020-10-10,2023-10-10. Used when time_range is ‘Custom’ or not set. | Optional |
| max_results | Maximum number of logs to return. Default is 50. | Optional |
| actor_object_id | Filter by user/actor ID (comma-separated for multiple). | Optional |
| ip_address | Filter by user IP address. | Optional |
| ordering | Order results. Possible values are: id, -id. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Brandefense.AuditLog.id | Number | Audit log ID. |
| Brandefense.AuditLog | Unknown | Audit log entry data. |
brandefense_get_domain_risk_assessment
Get third-party domain risk assessments from Brandefense.
Base Command
brandefense_get_domain_risk_assessment
Input
| Argument Name | Description | Required |
|---|---|---|
| uuid | Specific assessment UUID. Leave empty to list all. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Brandefense.DomainRiskAssessment.uuid | String | Assessment UUID. |
| Brandefense.DomainRiskAssessment | Unknown | Domain risk assessment data. |
brandefense_create_confirmed_phishing
Create a confirmed phishing address incident in Brandefense.
Base Command
brandefense_create_confirmed_phishing
Input
| Argument Name | Description | Required |
|---|---|---|
| url | The phishing URL to report. | Required |
| title | Title for the phishing incident. | Optional |
| network_type | Network type where phishing was found. Possible values are: DARK_WEB, SURFACE_WEB. | Optional |
| severity | Severity of the phishing incident. Possible values are: LOW, MEDIUM, HIGH, CRITICAL. | Optional |
| tags | Tags for the incident (comma-separated). | Optional |
| status | Initial status of the incident. Possible values are: OPEN, IN_PROGRESS, CLOSED. | Optional |
| asset_ids | Associated asset IDs (comma-separated). | Optional |
| data_source | Source of the phishing data. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Brandefense.ConfirmedPhishing | Unknown | Created confirmed phishing incident data. |
brandefense_takedown_request
Request takedown for a confirmed phishing address.
Base Command
brandefense_takedown_request
Input
| Argument Name | Description | Required |
|---|---|---|
| url | The phishing URL to request takedown for. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| Brandefense.TakedownRequest | Unknown | Takedown request response data. |
brandefense_get_indicators
Get indicators from Brandefense. Retrieves Consolidated Data and Incident indicators by type and organization with optional date range and status filters.
Base Command
brandefense_get_indicators
Input
| Argument Name | Description | Required |
|---|---|---|
| indicator_type | Type of indicator to retrieve. Each type has a different response body. Possible values are: leak, phishing_site, credit_card, cve, social_media, sensitive_file_disclosure, malicious-file, malicious_ads. | Required |
| organization_code | Organization code(s), comma-separated. Example: brandefense,other. | Optional |
| time_range | Predefined time range for filtering indicators. Select ‘Custom’ to use created_at_range instead. Possible values are: Last 24 Hours, Last 7 Days, Last 30 Days, Last 90 Days, Last 6 Months, Last 1 Year, Custom. | Optional |
| created_at_range | Custom date range (comma-separated start,end). Example: 2020-10-10,2023-10-10. Only used when time_range is ‘Custom’ or not set. | Optional |
| incident_status | Filter by incident status(es), comma-separated. Possible values are: OPEN, IN_PROGRESS, CLOSED, RISK_ACCEPTED, REJECTED. | Optional |
| page | Page number within the paginated result set. | Optional |
| page_size | Number of results per page (default values: 10, 20, 50, 100). | Optional |
| limit | Maximum total number of results to return. Default is 50. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Brandefense.Indicator.id | Number | Indicator ID. |
| Brandefense.Indicator.created_at | Date | Indicator creation date. |
| Brandefense.Indicator.content_object | Unknown | Indicator content data (varies by indicator type). |
| Brandefense.Indicator.content_object.data | String | Primary indicator value (URL, email, hash, etc.). |
| Brandefense.Indicator.content_object.username | String | Username associated with the indicator (leak type). |
| Brandefense.Indicator.content_object.password | String | Password associated with the indicator (leak type). |
| Brandefense.Indicator.content_object.source_platform | String | Source platform of the indicator. |
| Brandefense.Indicator.content_object.threat_actor | String | Threat actor associated with the indicator. |
| Brandefense.Indicator.content_object.breached_date | Date | Date when the breach occurred. |
| Brandefense.Indicator.threats | Unknown | Associated threats. |
| Brandefense.Indicator.threats.title | String | Threat title. |
| Brandefense.Indicator.threats.incidents.code | String | Incident code associated with the threat. |
| Brandefense.Indicator.threats.incidents.organization.name | String | Organization name associated with the incident. |
<~PLATFORM>
License Requirements
The following configuration parameters require one of these licenses: Cortex XSIAM or Agentix:
- Fetch incidents
</~PLATFORM>
Configuration parameters
url— Server URL (e.g. https://api.brandefense.io) (required)apikey— (required)insecure— Trust any certificate (not secure)proxy— Use system proxy settingsisFetch— Fetch incidentsincidentFetchInterval— Incidents Fetch Intervalfirst_fetch— First time fetchingincidentType— Incident typeMaxResults— Max Resultsmax_fetch— Maximum incidents per fetchintegrationReliability— Source ReliabilityIncidentCategory— Incident CategoryIncidentModule— Incident ModuleIncidentStatus— Incident StatusIntelligenceCategory— Intelligence CategoryIntelligenceSearch— Intelligence SearchFetchingIssueTypes— Fetching Issue Types (required)IncidentRules— Incident Rules
Commands (23)
-
brandefense_change_incident_statusChange the status of a Brandefense incident.
-
brandefense_create_confirmed_phishingCreate a confirmed phishing address incident in Brandefense.
-
brandefense_get_assetsGet list of monitored assets from Brandefense.
-
brandefense_get_audit_logsGet audit log entries from Brandefense.
-
brandefense_get_compromised_devicesGet compromised devices detected by Brandefense.
-
brandefense_get_domain_risk_assessmentGet third-party domain risk assessments from Brandefense.
-
brandefense_get_incident_detailGet detailed information for a specific Brandefense incident.
-
brandefense_get_incident_relativesGet related incidents for a specific Brandefense incident.
-
brandefense_get_incidentsGet Brandefense incidents with optional filtering by status, module, category, and time period.
-
brandefense_get_indicatorsGet indicators from Brandefense. Retrieves Consolidated Data and Incident indicators by type and organization with optional date range and status filters.
-
brandefense_get_intelligence_detailGet detailed information for a specific intelligence report.
-
brandefense_get_intelligence_rulesGet rules associated with a Brandefense intelligence report.
-
brandefense_get_intelligencesGet Brandefense intelligence reports with optional filtering.
-
brandefense_get_ioc_listFetch and consolidate all IoCs from the last N days (default 30). Pulls all IoC types and merges into a single list.
-
brandefense_get_iocsGet Indicators of Compromise from Brandefense threat intelligence feeds.
-
brandefense_incident_indicatorsGet indicators associated with a Brandefense incident.
-
brandefense_intelligence_indicatorsGet indicators associated with a Brandefense intelligence report.
-
brandefense_takedown_requestRequest takedown for a confirmed phishing address.
-
domainInvestigate a domain against Brandefense threat intelligence.
-
fileInvestigate a file hash against Brandefense threat intelligence.
-
ipInvestigate an IP address against Brandefense threat intelligence.
-
threat_searchPerform a CTI threat search and poll for results using ScheduledCommand.
-
urlInvestigate a URL against Brandefense threat intelligence.
import demistomock as demisto # noqa: F401 from CommonServerPython import * # noqa: F401 from CommonServerUserPython import * # noqa: F401 import json import traceback from datetime import datetime, timedelta, UTC from typing import Any import time import urllib3 # Disable insecure warnings urllib3.disable_warnings() """ CONSTANTS """ DATE_FORMAT = "%Y-%m-%dT%H:%M:%SZ" DATETIME_FORMAT = "%Y-%m-%dT%H:%M:%S.%fZ" MAX_INCIDENTS_TO_FETCH = 200 DEFAULT_PAGE_SIZE = 100 IOC_CACHE_TTL = 6 * 3600 # 6 hours in seconds — how long cached IoC results stay valid IOC_CACHE_MAX_SIZE = 5000 # Maximum number of cached IoC entries REQUEST_DELAY_SECONDS = 0.3 # 300ms delay between sequential API calls SEVERITY_MAP = { "INFO": IncidentSeverity.INFO, "LOW": IncidentSeverity.LOW, "MEDIUM": IncidentSeverity.MEDIUM, "HIGH": IncidentSeverity.HIGH, "CRITICAL": IncidentSeverity.CRITICAL, } RULES_DICT = { "Compromised Employee Account Detection": 1, "Compromised Client Account Detection": 2, "Executive Person Email Leak": 3, "Compromised Device Detection": 4, "Confirmed Phishing Address": 5, "Potential Phishing Address": 6, "Dark Web Intelligence": 7, "Sensitive File Disclosure": 8, "Confirmed Impersonated Account": 9, "Potential Impersonated Account": 10, "Vulnerable Technology Assessment": 13, "Stolen Credit/Debit Card Detection": 14, "Attack Surface": 15, "Custom Investigation": 16, "Malware Analysis": 17, "Fraud Protection": 18, "Other": 19, "SSL/TLS Vulnerability Detection": 20, "Vulnerability Detection": 21, "Insecure Redirect Protocol (HTTP) Detection": 22, "SSL/TLS Certificate Missing Domain Inclusion": 23, "SSL/TLS Weak Cipher & Algorithm Detection": 24, "Executive's Cyber Risk Assessment": 25, "Open DNS Resolver Detection": 26, "Malicious File Detection": 27, "Unsecure Login Page Detection": 28, "Exposed Redis Server": 29, "Exposed Memcached System": 30, "Vulnerable SSH Protocol Detection": 31, "Externally Exploitable Vulnerability Detection": 32, "DNS Server Allows Cache Snooping": 35, "Disclosure of Important Technology Information": 37, "Compromised Supply Chain Device": 38, "Security Scan": 40, "SMTP Open Relay Detection for Supplier Systems": 41, "Expired Supply Chain Domain Detection": 42, "Detection of Torrent Download Activity": 44, "Potentially Exposed SCADA Services": 45, "Credit Card": 46, "Potentially Vulnerable Exposed Technology": 47, "Domain Registrar Transfer Protection Not Enabled": 48, "Suspected Dark Web Exposure of Organization Asset": 49, "Data Sale Detection of Brand Accounts": 50, "Your Company Attacked by a Ransomware Group": 51, "Ransomware Attack Detected for Related Supply Chain Asset": 52, "Daily Discovered Entity Updates": 53, "Suspected Dark Web Exposure of Supply Chain Asset": 55, "Entity Found in Threat Intelligence Feeds": 56, "Malicious File Identified on Compromised Device": 57, "Compromised Employee Accounts via Botnet Attack": 58, "Unidentified Management Port Detection": 59, "Filtered Statused Management Port Exposure": 60, "Sensitive File Disclosure on GitHub Repositories": 61, "Sensitive File Disclosure on Postman Collections": 62, "Misconfigured AWS S3 Bucket Leading to Data Exposure": 63, "Misconfigured Azure Blob Storage Container Leading to Data Exposure": 64, "Misconfigured Google Cloud Storage Bucket Leading to Data Exposure": 65, "Misconfigured IBM Cloud Object Storage Leading to Data Exposure": 66, "Misconfigured Alibaba Cloud OSS Leading to Data Exposure": 67, "Misconfigured Backblaze B2 Bucket Leading to Data Exposure": 68, "Misconfigured DigitalOcean Space Leading to Data Exposure": 69, "Misconfigured Oracle Cloud Object Storage Leading to Data Exposure": 70, "DNS Zone Transfer Detection": 281, "DNSSEC Not Found": 282, "Shared Hosting Detection": 284, "SPF Misconfiguration": 285, "DMARC Not Found": 286, "DMARC Policy Not Configured": 287, "Domain Expires in 30 Days": 288, "Expired Domain Detection": 289, "SSL Expires in 30 Days": 290, "LDAP Server Allows Anonymous Bindings": 291, "Anonymous FTP Detection": 292, "SSH Supports Weak MAC Algorithms": 293, "SSH Supports Weak Ciphers": 294, "Expired SSL/TLS Detection": 295, "Blacklisted IP Address Detection": 296, "Blacklisted Domain Address Detection": 297, "Vulnerable HTTP Security Headers Detection": 299, "SMTP Open Relay Detection": 300, "Subdomain Takeover Detection": 301, "Private IP Address Exposure": 302, "Management Port Detection": 303, "Hacker Search Engine Monitoring Detection": 304, } """ HELPER FUNCTIONS """ def convert_to_demisto_severity(severity: str) -> float: """Maps Brandefense severity to Cortex XSOAR severity. Args: severity: Severity string from Brandefense API. Returns: Cortex XSOAR Severity (0 to 4). """ return SEVERITY_MAP.get(severity, IncidentSeverity.UNKNOWN) def list_to_comma_separated_string(lst: Any) -> str: """Convert a list to comma-separated string. Args: lst: Input list or string. Returns: Comma-separated string. """ if isinstance(lst, str): return lst if not lst: return "" return ",".join(str(element) for element in lst) def convert_rules_to_ids(rules: list[str]) -> str: """Convert rule names to their corresponding IDs. Args: rules: List of rule name strings. Returns: Comma-separated string of rule IDs. """ if not rules: return "" if not isinstance(rules, list): rules = [rules] ids = [str(RULES_DICT[key]) for key in rules if key in RULES_DICT] return ",".join(ids) TIME_RANGE_DAYS = { "Last 24 Hours": 1, "Last 7 Days": 7, "Last 30 Days": 30, "Last 90 Days": 90, "Last 6 Months": 180, "Last 1 Year": 365, } def resolve_time_range(args: dict[str, Any], custom_field: str = "created_at_range", fmt: str = "%Y-%m-%d") -> str | None: """Resolve a time_range dropdown or custom date field into a date range string. Args: args: Command arguments dict. custom_field: The name of the custom date range argument (fallback). fmt: strftime format for the dates. Returns: A comma-separated date range string (e.g. '2025-01-01,2026-02-22') or None. """ time_range = args.get("time_range") if time_range and time_range != "Custom": days = TIME_RANGE_DAYS.get(time_range) if days: today = datetime.now(UTC).strftime(fmt) start_date = (datetime.now(UTC) - timedelta(days=days)).strftime(fmt) return f"{start_date},{today}" custom_value = args.get(custom_field) if custom_value: return custom_value return None def hours_ago_from_epoch(epoch_time: int) -> int: """Calculate hours elapsed since a given epoch timestamp. Args: epoch_time: Unix epoch timestamp. Returns: Number of hours elapsed. """ current_time = datetime.now(UTC) past_time = datetime.fromtimestamp(epoch_time, UTC) time_difference = current_time - past_time return int(time_difference.total_seconds() / 3600) def get_first_time_fetch(first_fetch: str) -> int | None: """Parse first fetch time string to epoch timestamp. Args: first_fetch: Human-readable time string (e.g., '3 days'). Returns: Epoch timestamp or None. """ first_fetch_time = arg_to_datetime(arg=first_fetch if first_fetch else "3 days", arg_name="First fetch time", required=True) return int(first_fetch_time.timestamp()) if first_fetch_time else None def get_ioc_cache() -> dict[str, Any]: """Retrieve the IoC cache from integration context. Returns: Dictionary with cached IoC results keyed by 'type:value'. """ ctx = demisto.getIntegrationContext() return ctx.get("ioc_cache", {}) def set_ioc_cache(cache: dict[str, Any]) -> None: """Save the IoC cache to integration context. Trims to IOC_CACHE_MAX_SIZE if needed, removing oldest entries first. Args: cache: Dictionary of IoC cache entries. """ # Trim cache if it exceeds max size — keep newest entries if len(cache) > IOC_CACHE_MAX_SIZE: sorted_keys = sorted(cache.keys(), key=lambda k: cache[k].get("cached_at", 0)) keys_to_remove = sorted_keys[: len(cache) - IOC_CACHE_MAX_SIZE] for key in keys_to_remove: del cache[key] ctx = demisto.getIntegrationContext() ctx["ioc_cache"] = cache demisto.setIntegrationContext(ctx) def lookup_ioc_cache(ioc_type: str, value: str) -> dict[str, Any] | None: """Look up an IoC in the cache. Returns the cached result if found and not expired (within TTL). Args: ioc_type: Type of IoC (ip_address, domain, url, hash). value: The IoC value to look up. Returns: Cached result dict if valid, None if not found or expired. """ cache = get_ioc_cache() cache_key = f"{ioc_type}:{value}" entry = cache.get(cache_key) if not entry: return None cached_at = entry.get("cached_at", 0) now = int(datetime.now(UTC).timestamp()) if now - cached_at > IOC_CACHE_TTL: # Expired — remove from cache del cache[cache_key] set_ioc_cache(cache) demisto.debug(f"IoC cache expired for {cache_key}") return None demisto.debug(f"IoC cache HIT for {cache_key}") return entry.get("result") def update_ioc_cache(ioc_type: str, value: str, result: dict[str, Any]) -> None: """Store an IoC lookup result in the cache. Args: ioc_type: Type of IoC. value: The IoC value. result: The API response to cache. """ cache = get_ioc_cache() cache_key = f"{ioc_type}:{value}" cache[cache_key] = {"result": result, "cached_at": int(datetime.now(UTC).timestamp())} set_ioc_cache(cache) demisto.debug(f"IoC cache SET for {cache_key}") """ CLIENT CLASS """ class Client(BaseClient): """Client class to interact with the Brandefense API. Inherits from BaseClient which handles proxy, SSL verification, etc. Includes request throttling to prevent rate limit issues. """ def __init__(self, *args, request_delay: float = REQUEST_DELAY_SECONDS, **kwargs): """Initialize the client with optional request throttling. Args: request_delay: Seconds to wait between sequential API calls. """ super().__init__(*args, **kwargs) self._request_delay = request_delay self._last_request_time = 0.0 def _throttle(self) -> None: """Enforce minimum delay between API requests.""" if self._request_delay > 0: elapsed = time.time() - self._last_request_time if elapsed < self._request_delay: sleep_time = self._request_delay - elapsed time.sleep(sleep_time) # pylint: disable=E9003 self._last_request_time = time.time() def search_ioc(self, param: dict) -> str: """Search for Indicators of Compromise. Args: param: Query parameters including ioc_type and query. Returns: Raw text response from the API. """ self._throttle() return self._http_request(method="GET", params=param, url_suffix="/threat-intelligence/iocs/search", resp_type="text") def api_request(self, url: str, method: str, params: dict = None, data: dict = None) -> dict[str, Any]: """Make a generic API request with throttling. Args: url: API endpoint URL suffix. method: HTTP method (GET, POST, PATCH, etc.). params: Query parameters. data: Request body data. Returns: JSON response as dictionary. """ self._throttle() kwargs: dict[str, Any] = { "method": method, "url_suffix": url, "resp_type": "json", } if params: kwargs["params"] = params if data: kwargs["json_data"] = data return self._http_request(**kwargs) def get_incidents_list(self, params: dict) -> dict[str, Any]: """Get list of incidents with pagination support. Args: params: Query parameters for filtering. Returns: Paginated response with results. """ return self.api_request(url="/incidents", method="GET", params=params) def get_incident_detail(self, code: str) -> dict[str, Any]: """Get detailed information for a specific incident. Args: code: Incident code identifier. Returns: Incident details dictionary. """ return self.api_request(url=f"/incidents/{code}", method="GET") def get_incident_indicators(self, code: str, params: dict = None) -> dict[str, Any]: """Get indicators associated with an incident. Args: code: Incident code identifier. params: Optional pagination parameters. Returns: Paginated response with indicator results. """ return self.api_request(url=f"/incidents/{code}/indicators", method="GET", params=params) def get_incident_relatives(self, code: str) -> dict[str, Any]: """Get related incidents. Args: code: Incident code identifier. Returns: Related incidents data. """ return self.api_request(url=f"/incidents/{code}/relatives", method="GET") def change_incident_status(self, code: str, status: str) -> dict[str, Any]: """Change the status of an incident. Args: code: Incident code identifier. status: New status value. Returns: Updated incident data. """ return self.api_request(url=f"/incidents/{code}/change-status", method="PATCH", data={"status": status}) def get_intelligences_list(self, params: dict) -> dict[str, Any]: """Get list of intelligence reports. Args: params: Query parameters for filtering. Returns: Paginated response with results. """ return self.api_request(url="/intelligences", method="GET", params=params) def get_intelligence_detail(self, code: str) -> dict[str, Any]: """Get detailed information for a specific intelligence report. Args: code: Intelligence code identifier. Returns: Intelligence details dictionary. """ return self.api_request(url=f"/intelligences/{code}", method="GET") def get_intelligence_indicators(self, code: str, params: dict = None) -> dict[str, Any]: """Get indicators associated with an intelligence report. Args: code: Intelligence code identifier. params: Optional pagination parameters. Returns: Indicator results. """ return self.api_request(url=f"/intelligences/{code}/indicators", method="GET", params=params) def get_intelligence_rules(self, code: str) -> dict[str, Any]: """Get rules associated with an intelligence report. Args: code: Intelligence code identifier. Returns: Rules data. """ return self.api_request(url=f"/intelligences/{code}/rules", method="GET") def create_threat_search(self, value: str) -> dict[str, Any]: """Create a new threat search request. Args: value: Search value (domain, IP, hash, etc.). Returns: Response with UUID for tracking. """ return self.api_request(url="/cti/threat-search", method="POST", data={"value": value}) def get_threat_search_result(self, uuid: str) -> dict[str, Any]: """Get result of a threat search by UUID. Args: uuid: Threat search UUID. Returns: Threat search result. """ return self.api_request(url=f"/cti/threat-search/{uuid}", method="GET") def get_assets(self, params: dict = None) -> dict[str, Any]: """Get list of assets. Args: params: Query parameters for filtering. Returns: Paginated asset list. """ return self.api_request(url="/assets", method="GET", params=params) def get_asset_detail(self, asset_id: str) -> dict[str, Any]: """Get detail for a specific asset. Args: asset_id: Asset identifier. Returns: Asset details. """ return self.api_request(url=f"/assets/{asset_id}", method="GET") def get_iocs(self, params: dict) -> dict[str, Any]: """Get Indicators of Compromise from threat intelligence. Args: params: Query parameters including ioc_type. Returns: IoC list response. """ return self.api_request(url="/threat-intelligence/iocs", method="GET", params=params) def get_cti_rules(self, params: dict = None) -> dict[str, Any]: """Get CTI rules. Args: params: Query parameters for filtering. Returns: CTI rules list. """ return self.api_request(url="/threat-intelligence/rules", method="GET", params=params) def get_audit_logs(self, params: dict = None) -> dict[str, Any]: """Get audit log entries. Args: params: Query parameters for filtering. Returns: Paginated audit logs. """ return self.api_request(url="/audit-logs", method="GET", params=params) def get_compromised_devices(self, params: dict = None) -> dict[str, Any]: """Get compromised devices list. Args: params: Query parameters. Returns: Compromised devices data. """ return self.api_request(url="/compromised-devices", method="GET", params=params) def get_compromised_device_detail(self, botnet_id: str) -> dict[str, Any]: """Get detail for a specific compromised device. Args: botnet_id: Botnet/compromised device identifier. Returns: Device details. """ return self.api_request(url=f"/compromised-devices/{botnet_id}", method="GET") def create_confirmed_phishing(self, data: dict) -> dict[str, Any]: """Create a confirmed phishing address incident. Args: data: Phishing incident data. Returns: Created incident response. """ return self.api_request(url="/incidents/confirmed-phishing-address", method="POST", data=data) def takedown_request(self, data: dict) -> dict[str, Any]: """Request takedown for a confirmed phishing address. Args: data: Takedown request data containing URL. Returns: Takedown request response. """ return self.api_request(url="/indicators/confirmed-phishing-address/takedown-request", method="POST", data=data) def get_domain_risk_assessments(self, params: dict = None) -> dict[str, Any]: """Get third-party domain risk assessments. Args: params: Query parameters. Returns: Risk assessment list. """ return self.api_request(url="/third-party-risk-management", method="GET", params=params) def get_domain_risk_assessment_detail(self, uuid: str) -> dict[str, Any]: """Get detail for a specific domain risk assessment. Args: uuid: Assessment UUID. Returns: Assessment details. """ return self.api_request(url=f"/third-party-risk-management/{uuid}", method="GET") def get_indicators(self, params: dict = None) -> dict[str, Any]: """Get indicators by type from the Brandefense Indicators endpoint. Supports indicator types: leak, phishing_site, credit_card, cve, social_media, sensitive_file_disclosure, malicious-file, malicious_ads. Args: params: Query parameters including indicator_type (required) and organization__code__in (required). Returns: Paginated indicator results. """ return self.api_request(url="/indicators", method="GET", params=params) """ PAGINATION """ def paginate( client: Client, url: str, method: str, params: dict = None, data: dict = None, max_results: int = DEFAULT_PAGE_SIZE ) -> list[dict[str, Any]]: """Generic pagination function for Brandefense API. Follows cursor-based pagination using the 'next' field. Args: client: Brandefense API client. url: API endpoint URL suffix. method: HTTP method. params: Query parameters. data: Request body data. max_results: Maximum number of results to return. Returns: List of result items. """ if not params: params = { "page_size": DEFAULT_PAGE_SIZE, "page": 1, } all_results: list[dict[str, Any]] = [] has_more = True while has_more: response = client.api_request(url=url, method=method, params=params, data=data) if not response or "results" not in response: break results = response.get("results", []) if not results: break all_results.extend(results) if len(all_results) >= max_results: break next_url = response.get("next") if next_url: params["page"] = params.get("page", 1) + 1 else: has_more = False return all_results[:max_results] """ COMMAND FUNCTIONS """ def test_module(client: Client) -> str: """Tests API connectivity and authentication. Args: client: Brandefense API client. Returns: 'ok' if test passed. """ try: params = {"ioc_type": "ip_address", "period": "24h"} client.get_iocs(params=params) return "ok" except DemistoException as e: if "Forbidden" in str(e) or "403" in str(e): return "Authorization Error: make sure API Key is correctly set" elif "401" in str(e): return "Authorization Error: invalid API key" else: raise e VENDOR_NAME = "Brandefense" DEFAULT_RELIABILITY = DBotScoreReliability.B # Usually reliable def severity_to_dbot_score(severity: str | None) -> int: """Map Brandefense severity to DBot score. Args: severity: Brandefense severity string. Returns: DBot score: 0 (Unknown), 1 (Good), 2 (Suspicious), 3 (Malicious). """ if not severity: return Common.DBotScore.NONE severity_upper = severity.upper() if severity_upper in ("HIGH", "CRITICAL"): return Common.DBotScore.BAD elif severity_upper in ("MEDIUM",): return Common.DBotScore.SUSPICIOUS elif severity_upper in ("LOW", "INFO"): return Common.DBotScore.GOOD return Common.DBotScore.NONE def cached_ioc_lookup(client: Client, ioc_type: str, value: str) -> dict[str, Any]: """Look up an IoC with caching to prevent redundant API calls. Checks the integration context cache first. On miss, queries the API and stores the result for future lookups (TTL: 6 hours). Args: client: Brandefense API client. ioc_type: IoC type (ip_address, domain, url, hash). value: The indicator value to look up. Returns: The IoC result dict (may be empty if not found). """ # Check cache first cached = lookup_ioc_cache(ioc_type, value) if cached is not None: return cached # Cache miss — call the API param = {"query": value, "ioc_type": ioc_type} raw_response = client.search_ioc(param=param) results = json.loads(raw_response) if raw_response else [] if results and isinstance(results, list) and len(results) > 0: result = results[0] else: result = {} # Store in cache for future lookups update_ioc_cache(ioc_type, value, result) return result def search_ip_command(client: Client, args: dict[str, Any]) -> list[CommandResults]: """Search for malicious IP addresses in Brandefense threat intelligence. Returns proper DBotScore and IP standard context so XSOAR auto-enrichment includes Brandefense in all TI lookups. Args: client: Brandefense API client. args: Command arguments containing 'ip'. Returns: List of CommandResults with DBotScore and IP indicator. """ ips = argToList(args.get("ip")) reliability = args.get("reliability", DEFAULT_RELIABILITY) command_results: list[CommandResults] = [] for ip_value in ips: result = cached_ioc_lookup(client, "ip_address", ip_value) if result: severity = result.get("severity") score = severity_to_dbot_score(severity) description = result.get("category", "Found in Brandefense threat intelligence") else: result = {} score = Common.DBotScore.NONE description = None dbot_score = Common.DBotScore( indicator=ip_value, indicator_type=DBotScoreType.IP, integration_name=VENDOR_NAME, score=score, reliability=reliability, message=description, ) ip_indicator = Common.IP( ip=ip_value, dbot_score=dbot_score, ) readable_output = tableToMarkdown( f"Brandefense IP Reputation - {ip_value}", { "IP": ip_value, "Score": score, "Severity": result.get("severity", "N/A"), "Category": result.get("category", "N/A"), "First Seen": result.get("first_seen", "N/A"), "Last Seen": result.get("last_seen", "N/A"), }, ) command_results.append( CommandResults( readable_output=readable_output, indicator=ip_indicator, outputs_prefix="Brandefense.IP", outputs_key_field="data", outputs=result if result else None, raw_response=result, ) ) return command_results def search_domain_command(client: Client, args: dict[str, Any]) -> list[CommandResults]: """Search for malicious domains in Brandefense threat intelligence. Returns proper DBotScore and Domain standard context so XSOAR auto-enrichment includes Brandefense in all TI lookups. Args: client: Brandefense API client. args: Command arguments containing 'domain'. Returns: List of CommandResults with DBotScore and Domain indicator. """ domains = argToList(args.get("domain")) reliability = args.get("reliability", DEFAULT_RELIABILITY) command_results: list[CommandResults] = [] for domain_value in domains: result = cached_ioc_lookup(client, "domain", domain_value) if result: severity = result.get("severity") score = severity_to_dbot_score(severity) description = result.get("category", "Found in Brandefense threat intelligence") else: result = {} score = Common.DBotScore.NONE description = None dbot_score = Common.DBotScore( indicator=domain_value, indicator_type=DBotScoreType.DOMAIN, integration_name=VENDOR_NAME, score=score, reliability=reliability, message=description, ) domain_indicator = Common.Domain( domain=domain_value, dbot_score=dbot_score, ) readable_output = tableToMarkdown( f"Brandefense Domain Reputation - {domain_value}", { "Domain": domain_value, "Score": score, "Severity": result.get("severity", "N/A"), "Category": result.get("category", "N/A"), "First Seen": result.get("first_seen", "N/A"), "Last Seen": result.get("last_seen", "N/A"), }, ) command_results.append( CommandResults( readable_output=readable_output, indicator=domain_indicator, outputs_prefix="Brandefense.Domain", outputs_key_field="data", outputs=result if result else None, raw_response=result, ) ) return command_results def search_hash_command(client: Client, args: dict[str, Any]) -> list[CommandResults]: """Search for malware file hashes in Brandefense threat intelligence. Returns proper DBotScore and File standard context so XSOAR auto-enrichment includes Brandefense in all TI lookups. Args: client: Brandefense API client. args: Command arguments containing 'file' (hash). Returns: List of CommandResults with DBotScore and File indicator. """ file_hashes = argToList(args.get("file", args.get("hash", ""))) reliability = args.get("reliability", DEFAULT_RELIABILITY) command_results: list[CommandResults] = [] for file_hash in file_hashes: result = cached_ioc_lookup(client, "hash", file_hash) if result: severity = result.get("severity") score = severity_to_dbot_score(severity) description = result.get("category", "Found in Brandefense threat intelligence") else: result = {} score = Common.DBotScore.NONE description = None dbot_score = Common.DBotScore( indicator=file_hash, indicator_type=DBotScoreType.FILE, integration_name=VENDOR_NAME, score=score, reliability=reliability, message=description, ) # Determine hash type by length hash_type_kwargs: dict[str, Any] = {"dbot_score": dbot_score} if len(file_hash) == 32: hash_type_kwargs["md5"] = file_hash elif len(file_hash) == 40: hash_type_kwargs["sha1"] = file_hash elif len(file_hash) == 64: hash_type_kwargs["sha256"] = file_hash file_indicator = Common.File(**hash_type_kwargs) readable_output = tableToMarkdown( f"Brandefense File Reputation - {file_hash}", { "Hash": file_hash, "Score": score, "Severity": result.get("severity", "N/A"), "Category": result.get("category", "N/A"), "First Seen": result.get("first_seen", "N/A"), "Last Seen": result.get("last_seen", "N/A"), }, ) command_results.append( CommandResults( readable_output=readable_output, indicator=file_indicator, outputs_prefix="Brandefense.File", outputs_key_field="data", outputs=result if result else None, raw_response=result, ) ) return command_results def search_url_command(client: Client, args: dict[str, Any]) -> list[CommandResults]: """Search for malicious URLs in Brandefense threat intelligence. Returns proper DBotScore and URL standard context so XSOAR auto-enrichment includes Brandefense in all TI lookups. Args: client: Brandefense API client. args: Command arguments containing 'url'. Returns: List of CommandResults with DBotScore and URL indicator. """ urls = argToList(args.get("url")) reliability = args.get("reliability", DEFAULT_RELIABILITY) command_results: list[CommandResults] = [] for url_value in urls: result = cached_ioc_lookup(client, "url", url_value) if result: severity = result.get("severity") score = severity_to_dbot_score(severity) description = result.get("category", "Found in Brandefense threat intelligence") else: result = {} score = Common.DBotScore.NONE description = None dbot_score = Common.DBotScore( indicator=url_value, indicator_type=DBotScoreType.URL, integration_name=VENDOR_NAME, score=score, reliability=reliability, message=description, ) url_indicator = Common.URL( url=url_value, dbot_score=dbot_score, ) readable_output = tableToMarkdown( f"Brandefense URL Reputation - {url_value}", { "URL": url_value, "Score": score, "Severity": result.get("severity", "N/A"), "Category": result.get("category", "N/A"), "First Seen": result.get("first_seen", "N/A"), "Last Seen": result.get("last_seen", "N/A"), }, ) command_results.append( CommandResults( readable_output=readable_output, indicator=url_indicator, outputs_prefix="Brandefense.URL", outputs_key_field="data", outputs=result if result else None, raw_response=result, ) ) return command_results def get_incidents_command(client: Client, args: dict[str, Any]) -> CommandResults: """Get Brandefense incidents with optional filtering. Args: client: Brandefense API client. args: Command arguments for filtering. Returns: CommandResults with incident list. """ incident_category = list_to_comma_separated_string(args.get("module_category")) incident_module = list_to_comma_separated_string(args.get("module")) incident_rules = args.get("IncidentRules") status = list_to_comma_separated_string(args.get("status", "OPEN")) period = int(args.get("period", 1)) max_results = int(args.get("MaxResults", DEFAULT_PAGE_SIZE)) template_ids = convert_rules_to_ids(incident_rules) if incident_rules else "" # Resolve time range: dropdown > custom date > period (hours) fallback date_range = resolve_time_range(args, fmt="%Y-%m-%d %H:%M:%S") if not date_range: current_datetime = datetime.now(UTC) start_datetime = current_datetime - timedelta(hours=period) date_range = f'{start_datetime.strftime("%Y-%m-%d %H:%M:%S")},{current_datetime.strftime("%Y-%m-%d %H:%M:%S")}' params = { "page_size": DEFAULT_PAGE_SIZE, "created_at__range": date_range, "status": status.upper() if status else "OPEN", "page": 1, } if incident_module: params["module"] = incident_module if incident_category: params["module_category"] = incident_category if template_ids: params["template_id__in"] = template_ids if args.get("search"): params["search"] = args["search"] if args.get("severity"): params["severity"] = args["severity"] if args.get("tags"): params["tags"] = args["tags"] if args.get("network_type"): params["network_type"] = args["network_type"] if args.get("mitre_tactics"): params["mitre_tactics"] = args["mitre_tactics"] if args.get("ordering"): params["ordering"] = args["ordering"] if args.get("has_indicator"): params["has_indicator"] = args["has_indicator"] if args.get("has_attachment"): params["has_attachment"] = args["has_attachment"] if args.get("type"): params["type"] = args["type"] incidents = paginate(client, url="/incidents", method="GET", params=params, max_results=max_results) for _incident in incidents: code = _incident.get("code") if code: _incident["reference_url"] = f"https://app.brandefense.io/issues/incidents/all/{code}" readable_output = tableToMarkdown( "Brandefense Incidents", incidents, headers=["code", "title", "severity", "status", "created_at", "reference_url"], removeNull=True, ) return CommandResults( readable_output=readable_output, outputs_prefix="Brandefense.Incident", outputs_key_field="code", outputs=incidents ) def get_incident_detail_command(client: Client, args: dict[str, Any]) -> CommandResults: """Get detailed information for a specific Brandefense incident. Args: client: Brandefense API client. args: Command arguments containing 'code'. Returns: CommandResults with incident details. """ code = args.get("code", "") incident = client.get_incident_detail(code) incident["reference_url"] = f"https://app.brandefense.io/issues/incidents/all/{code}" readable_output = tableToMarkdown("Incident Details", incident) return CommandResults( readable_output=readable_output, outputs_prefix="Brandefense.IncidentDetail", outputs_key_field="code", outputs=incident ) def get_incident_indicators_command(client: Client, args: dict[str, Any]) -> CommandResults: """Get indicators associated with a Brandefense incident. Args: client: Brandefense API client. args: Command arguments containing 'code'. Returns: CommandResults with indicator list. """ code = args.get("code", "") indicators = paginate(client, url=f"/incidents/{code}/indicators", method="GET") readable_output = tableToMarkdown("Incident Indicators", indicators, removeNull=True) return CommandResults( readable_output=readable_output, outputs_prefix="Brandefense.Incident.Indicators", outputs_key_field="code", outputs=indicators, ) def get_incident_relatives_command(client: Client, args: dict[str, Any]) -> CommandResults: """Get related incidents for a Brandefense incident. Args: client: Brandefense API client. args: Command arguments containing 'code'. Returns: CommandResults with related incidents. """ code = args.get("code", "") relatives = client.get_incident_relatives(code) readable_output = tableToMarkdown("Related Incidents", relatives) return CommandResults(readable_output=readable_output, outputs_prefix="Brandefense.Incident.Relatives", outputs=relatives) def change_incident_status_command(client: Client, args: dict[str, Any]) -> CommandResults: """Change the status of a Brandefense incident. Args: client: Brandefense API client. args: Command arguments containing 'code' and 'status'. Returns: CommandResults with updated incident. """ code = args.get("code", "") status = args.get("status", "").upper() valid_statuses = ["OPEN", "IN_PROGRESS", "CLOSED", "RISK_ACCEPTED", "REJECTED"] if status not in valid_statuses: raise ValueError(f'Invalid status "{status}". Must be one of: {", ".join(valid_statuses)}') result = client.change_incident_status(code, status) readable_output = tableToMarkdown("Incident Status Updated", result) return CommandResults(readable_output=readable_output, outputs_prefix="Brandefense.ChangingStatus", outputs=result) def get_intelligence_command(client: Client, args: dict[str, Any]) -> CommandResults: """Get Brandefense intelligence reports with optional filtering. Args: client: Brandefense API client. args: Command arguments for filtering. Returns: CommandResults with intelligence list. """ category = list_to_comma_separated_string(args.get("category")) search_term = args.get("search", "") period = int(args.get("period", 24)) max_results = int(args.get("MaxResults", DEFAULT_PAGE_SIZE)) # Resolve time range: dropdown > custom date > period (hours) fallback date_range = resolve_time_range(args, fmt="%Y-%m-%d %H:%M:%S") if not date_range: current_datetime = datetime.now(UTC) start_datetime = current_datetime - timedelta(hours=period) date_range = f'{start_datetime.strftime("%Y-%m-%d %H:%M:%S")},{current_datetime.strftime("%Y-%m-%d %H:%M:%S")}' params = { "page_size": DEFAULT_PAGE_SIZE, "created_at__range": date_range, "page": 1, } if category: params["category__in"] = category intelligence_list = paginate(client, url="/intelligences", method="GET", params=params, max_results=max_results) # Filter by search term in tags if provided if search_term: intelligence_list = [ item for item in intelligence_list if any(search_term.lower() in tag.lower() for tag in (item.get("tags") or [])) ] for intel in intelligence_list: code = intel.get("code") if code: intel["reference_url"] = f"https://app.brandefense.io/issues/intelligence/all/{code}" readable_output = tableToMarkdown( "Brandefense Intelligence", intelligence_list, headers=["code", "title", "severity", "created_at", "reference_url"], removeNull=True, ) return CommandResults( readable_output=readable_output, outputs_prefix="Brandefense.Intelligence", outputs_key_field="code", outputs=intelligence_list, ) def get_intelligence_detail_command(client: Client, args: dict[str, Any]) -> CommandResults: """Get detailed information for a specific intelligence report. Args: client: Brandefense API client. args: Command arguments containing 'code'. Returns: CommandResults with intelligence details. """ code = args.get("code", "") intelligence = client.get_intelligence_detail(code) intelligence["reference_url"] = f"https://app.brandefense.io/issues/intelligence/all/{code}" readable_output = tableToMarkdown("Intelligence Details", intelligence) return CommandResults( readable_output=readable_output, outputs_prefix="Brandefense.IntelligenceDetail", outputs_key_field="code", outputs=intelligence, ) def get_intelligence_indicators_command(client: Client, args: dict[str, Any]) -> CommandResults: """Get indicators associated with an intelligence report. Args: client: Brandefense API client. args: Command arguments containing 'code'. Returns: CommandResults with indicator list. """ code = args.get("code", "") response = client.get_intelligence_indicators(code) # API may return a list directly or a paginated dict if isinstance(response, list): indicators = response elif isinstance(response, dict): indicators = response.get("results", []) else: indicators = [] readable_output = tableToMarkdown("Intelligence Indicators", indicators, removeNull=True) return CommandResults( readable_output=readable_output, outputs_prefix="Brandefense.Intelligence.Indicators", outputs_key_field="code", outputs=indicators, ) def get_intelligence_rules_command(client: Client, args: dict[str, Any]) -> CommandResults: """Get rules associated with an intelligence report. Args: client: Brandefense API client. args: Command arguments containing 'code'. Returns: CommandResults with rules data. """ code = args.get("code", "") rules = client.get_intelligence_rules(code) readable_output = tableToMarkdown("Intelligence Rules", rules) return CommandResults(readable_output=readable_output, outputs_prefix="Brandefense.Intelligence.Rules", outputs=rules) def threat_search_command(client: Client, args: dict[str, Any]) -> CommandResults: """Perform a threat search using XSOAR ScheduledCommand polling. Args: client: Brandefense API client. args: Command arguments containing 'value', 'interval_in_seconds', 'timeout_in_seconds'. On subsequent polls, 'uuid' is provided by the ScheduledCommand. Returns: CommandResults with threat search results, or a ScheduledCommand for the next poll. """ value = args.get("value", "") uuid = args.get("uuid") interval_in_seconds = int(args.get("interval_in_seconds", 20)) timeout_in_seconds = int(args.get("timeout_in_seconds", 600)) if not uuid: # First run: create the threat search create_response = client.create_threat_search(value) uuid = create_response.get("uuid") if not uuid: raise DemistoException("Failed to create threat search: no UUID returned") # Check for results response = client.get_threat_search_result(uuid) result = response.get("result") if not result: # No result yet - schedule the next poll polling_args = { "value": value, "uuid": uuid, "interval_in_seconds": interval_in_seconds, "timeout_in_seconds": timeout_in_seconds, } scheduled_command = ScheduledCommand( command="threat_search", next_run_in_seconds=interval_in_seconds, args=polling_args, timeout_in_seconds=timeout_in_seconds, ) return CommandResults( readable_output=f"Threat search is running. Waiting for results (UUID: {uuid}).", scheduled_command=scheduled_command, ) # Flatten results extras = result.pop("results", {}) for key, value_data in extras.items(): if isinstance(value_data, list) and len(value_data) > 0: if isinstance(value_data[0], dict): for k, v in value_data[0].items(): response[k] = v else: response[key] = value_data[0] else: response[key] = value_data readable_output = tableToMarkdown("Threat Search Results", response) return CommandResults( readable_output=readable_output, outputs_prefix="Brandefense.ThreatSearch", outputs_key_field="uuid", outputs=response ) def get_assets_command(client: Client, args: dict[str, Any]) -> CommandResults: """Get Brandefense assets list. Args: client: Brandefense API client. args: Command arguments for filtering. Returns: CommandResults with asset list. """ params: dict[str, Any] = {} if args.get("type"): params["type__in"] = args["type"] if args.get("severity"): params["severity__in"] = args["severity"] if args.get("status"): params["status__in"] = args["status"] if args.get("search"): params["search"] = args["search"] if args.get("module"): params["module__code__in"] = args["module"] if args.get("ordering"): params["ordering"] = args["ordering"] date_range = resolve_time_range(args) if date_range: params["created_at_range"] = date_range if args.get("threat_type"): params["threat_type"] = args["threat_type"] if args.get("asset_ilike"): params["asset__ilike"] = args["asset_ilike"] if args.get("organization"): params["organization__code__in"] = args["organization"] max_results = int(args.get("max_results", 50)) params["page_size"] = DEFAULT_PAGE_SIZE params["page"] = 1 assets = paginate(client, url="/assets", method="GET", params=params, max_results=max_results) readable_output = tableToMarkdown( "Brandefense Assets", assets, headers=["id", "asset", "type", "severity", "status"], removeNull=True ) return CommandResults( readable_output=readable_output, outputs_prefix="Brandefense.Asset", outputs_key_field="id", outputs=assets ) def get_iocs_command(client: Client, args: dict[str, Any]) -> CommandResults: """Get Indicators of Compromise from Brandefense threat intelligence. Args: client: Brandefense API client. args: Command arguments containing 'ioc_type'. Returns: CommandResults with IoC list. """ ioc_type = args.get("ioc_type") period = args.get("period", "24h") params: dict[str, Any] = {"ioc_type": ioc_type} if period: params["period"] = period if args.get("exclude_country"): params["exclude_country"] = args["exclude_country"] if args.get("include_country"): params["include_country"] = args["include_country"] if args.get("module"): params["module"] = args["module"] response = client.get_iocs(params=params) results = response.get("results", []) if isinstance(response, dict) else response readable_output = tableToMarkdown(f"Brandefense IoCs ({ioc_type})", results, removeNull=True) return CommandResults( readable_output=readable_output, outputs_prefix="Brandefense.IOC", outputs_key_field="data", outputs=results ) def get_ioc_list_command(client: Client, args: dict[str, Any]) -> CommandResults: """Fetch and consolidate all IoCs from the last N days (default 30). Pulls IoCs of all 4 types (ip_address, domain, url, hash) and merges them into a single list. Useful for building a local threat feed or exporting to a SIEM/EDR. Args: client: Brandefense API client. args: Command arguments. - days: Number of days to look back (default: 30, max: 90). - ioc_type: Optional, filter to specific type(s). - limit: Maximum total results to return (default: 5000). Returns: CommandResults with consolidated IoC list. """ days = min(int(args.get("days", 30)), 90) limit = int(args.get("limit", 5000)) requested_types = argToList(args.get("ioc_type", "ip_address,domain,url,hash")) all_iocs: list[dict[str, Any]] = [] period = f"{days * 24}h" # Convert days to hours ioc_type_map = { "ip_address": "IP Address", "ip": "IP Address", "domain": "Domain", "url": "URL", "hash": "File Hash", } for ioc_type in requested_types: # Normalize type name normalized_type = ioc_type.lower().strip() if normalized_type == "ip": normalized_type = "ip_address" try: params: dict[str, Any] = {"ioc_type": normalized_type, "period": period} response = client.get_iocs(params=params) results = response.get("results", []) if isinstance(response, dict) else response if isinstance(results, list): for item in results: item["ioc_type"] = normalized_type item["ioc_type_display"] = ioc_type_map.get(normalized_type, normalized_type) all_iocs.extend(results) except Exception as e: demisto.debug(f"Error fetching IoCs of type {normalized_type}: {str(e)}") continue if len(all_iocs) >= limit: break # Trim to limit all_iocs = all_iocs[:limit] # Summary stats type_counts: dict[str, int] = {} for ioc in all_iocs: t = ioc.get("ioc_type_display", "Unknown") type_counts[t] = type_counts.get(t, 0) + 1 summary_rows = [{"Type": k, "Count": v} for k, v in type_counts.items()] summary_rows.append({"Type": "**Total**", "Count": len(all_iocs)}) readable_output = tableToMarkdown(f"Brandefense IoC List (Last {days} Days)", summary_rows, headers=["Type", "Count"]) readable_output += "\n\n" readable_output += tableToMarkdown( "IoC Details (showing first 50)", all_iocs[:50], headers=["ioc_type_display", "data", "severity", "first_seen", "last_seen"], headerTransform=lambda h: { "ioc_type_display": "Type", "data": "Value", "severity": "Severity", "first_seen": "First Seen", "last_seen": "Last Seen", }.get(h, h), removeNull=True, ) return CommandResults( readable_output=readable_output, outputs_prefix="Brandefense.IOCList", outputs_key_field="data", outputs=all_iocs ) def get_indicators_command(client: Client, args: dict[str, Any]) -> CommandResults: """Get indicators from Brandefense Indicators endpoint. Retrieves Consolidated Data and Incident indicators filtered by type and organization, with optional date range and status filters. Args: client: Brandefense API client. args: Command arguments. - indicator_type: Required. One of: leak, phishing_site, credit_card, cve, social_media, sensitive_file_disclosure, malicious-file, malicious_ads. - organization_code: Required. Organization code(s), comma-separated. - created_at_range: Date range filter (e.g., 2020-10-10,2023-10-10). - incident_status: Filter by incident status(es), comma-separated. - page: Page number. - page_size: Results per page. - limit: Maximum total results to return (default 50). Returns: CommandResults with indicator list. """ indicator_type = args.get("indicator_type") if not indicator_type: raise ValueError("indicator_type is a required argument.") params: dict[str, Any] = { "indicator_type": indicator_type, } if args.get("organization_code"): params["organization__code__in"] = args["organization_code"] date_range = resolve_time_range(args) if date_range: params["created_at__range"] = date_range if args.get("incident_status"): params["incident__status__in"] = args["incident_status"] page = args.get("page") page_size = args.get("page_size") limit = int(args.get("limit", 50)) if page: params["page"] = int(page) if page_size: params["page_size"] = int(page_size) if not page_size and not page: params["page_size"] = min(limit, 100) response = client.get_indicators(params=params) if isinstance(response, dict): results = response.get("results", []) total_count = response.get("count", len(results)) elif isinstance(response, list): results = response total_count = len(results) else: results = [] total_count = 0 # Truncate to limit results = results[:limit] # Build readable output # Flatten content_object fields into top-level for display display_rows = [] for item in results: row: dict[str, Any] = {"id": item.get("id"), "created_at": item.get("created_at")} content_obj = item.get("content_object", {}) if isinstance(content_obj, dict): for key, val in content_obj.items(): row[key] = val # Add threat/incident info threats = item.get("threats", []) if threats: incident_codes = [] for threat in threats: for inc in threat.get("incidents", []): incident_codes.append(inc.get("code", "")) row["incidents"] = ", ".join(incident_codes) row["threat_title"] = threats[0].get("title", "") display_rows.append(row) readable_output = tableToMarkdown( f"Brandefense Indicators ({indicator_type}) - {total_count} total", display_rows, removeNull=True ) return CommandResults( readable_output=readable_output, outputs_prefix="Brandefense.Indicator", outputs_key_field="id", outputs=results, raw_response=response, ) def get_compromised_devices_command(client: Client, args: dict[str, Any]) -> CommandResults: """Get compromised devices from Brandefense. Args: client: Brandefense API client. args: Command arguments. Returns: CommandResults with compromised devices list. """ botnet_id = args.get("botnet_id") if botnet_id: result = client.get_compromised_device_detail(botnet_id) results = [result] if isinstance(result, dict) else result else: max_results = int(args.get("max_results", 10)) params: dict[str, Any] = {"page_size": 10, "page": 1} if args.get("username"): params["username__contains"] = args["username"] date_range = resolve_time_range(args, custom_field="detection_date_range") if date_range: params["detection_date__range"] = date_range if args.get("search"): params["search"] = args["search"] if args.get("ordering"): params["ordering"] = args["ordering"] results = paginate(client, url="/compromised-devices", method="GET", params=params, max_results=max_results) readable_output = tableToMarkdown("Compromised Devices", results, removeNull=True) return CommandResults( readable_output=readable_output, outputs_prefix="Brandefense.CompromisedDevice", outputs_key_field="id", outputs=results ) def get_audit_logs_command(client: Client, args: dict[str, Any]) -> CommandResults: """Get audit log entries from Brandefense. Args: client: Brandefense API client. args: Command arguments for filtering. Returns: CommandResults with audit logs. """ params: dict[str, Any] = {"page_size": DEFAULT_PAGE_SIZE, "page": 1} if args.get("type"): params["type"] = args["type"] if args.get("search"): params["search"] = args["search"] date_range = resolve_time_range(args) if date_range: params["created_at__range"] = date_range if args.get("actor_object_id"): params["actor_object_id__in"] = args["actor_object_id"] if args.get("ip_address"): params["ip_address"] = args["ip_address"] if args.get("ordering"): params["ordering"] = args["ordering"] max_results = int(args.get("max_results", 50)) logs = paginate(client, url="/audit-logs", method="GET", params=params, max_results=max_results) readable_output = tableToMarkdown("Audit Logs", logs, removeNull=True) return CommandResults( readable_output=readable_output, outputs_prefix="Brandefense.AuditLog", outputs_key_field="id", outputs=logs ) def create_confirmed_phishing_command(client: Client, args: dict[str, Any]) -> CommandResults: """Create a confirmed phishing address incident. Args: client: Brandefense API client. args: Command arguments. Returns: CommandResults with created incident data. """ data: dict[str, Any] = { "data": args.get("url"), } if args.get("title"): data["title"] = args["title"] if args.get("network_type"): data["network_type"] = args["network_type"] if args.get("severity"): data["severity"] = args["severity"] if args.get("tags"): data["tags"] = argToList(args["tags"]) if args.get("status"): data["status"] = args["status"] if args.get("asset_ids"): data["asset_ids"] = [int(i) for i in argToList(args["asset_ids"])] if args.get("data_source"): data["data_source"] = args["data_source"] response = client.create_confirmed_phishing(data) readable_output = tableToMarkdown("Created Confirmed Phishing Incident", response, removeNull=True) return CommandResults(readable_output=readable_output, outputs_prefix="Brandefense.ConfirmedPhishing", outputs=response) def takedown_request_command(client: Client, args: dict[str, Any]) -> CommandResults: """Request takedown for a confirmed phishing address. Args: client: Brandefense API client. args: Command arguments containing 'url'. Returns: CommandResults with takedown request response. """ url_value = args.get("url") data = {"data": url_value} response = client.takedown_request(data) readable_output = tableToMarkdown("Takedown Request", response, removeNull=True) return CommandResults(readable_output=readable_output, outputs_prefix="Brandefense.TakedownRequest", outputs=response) def get_domain_risk_assessment_command(client: Client, args: dict[str, Any]) -> CommandResults: """Get third-party domain risk assessments. Args: client: Brandefense API client. args: Command arguments. Returns: CommandResults with risk assessment data. """ uuid = args.get("uuid") if uuid: result = client.get_domain_risk_assessment_detail(uuid) results = [result] if isinstance(result, dict) else result else: response = client.get_domain_risk_assessments() results = response.get("results", []) if isinstance(response, dict) else response readable_output = tableToMarkdown("Domain Risk Assessments", results, removeNull=True) return CommandResults( readable_output=readable_output, outputs_prefix="Brandefense.DomainRiskAssessment", outputs_key_field="uuid", outputs=results, ) """ FETCH INCIDENTS """ def get_brandefense_incidents_for_fetch(client: Client, args: dict[str, Any]) -> list[dict[str, Any]]: """Internal function to fetch incidents for the fetch-incidents command. Args: client: Brandefense API client. args: Parameters for filtering. Returns: List of incident details with indicators. """ incident_category = list_to_comma_separated_string(args.get("IncidentCategory")) incident_module = list_to_comma_separated_string(args.get("IncidentModule")) incident_rules = args.get("IncidentRules") status = list_to_comma_separated_string(args.get("status", "OPEN")) period = int(args.get("period", 1)) max_results = int(args.get("MaxResults", DEFAULT_PAGE_SIZE)) template_ids = convert_rules_to_ids(incident_rules) if incident_rules else "" current_datetime = datetime.now(UTC) start_datetime = current_datetime - timedelta(hours=period) params = { "page_size": DEFAULT_PAGE_SIZE, "created_at__range": f'{start_datetime.strftime("%Y-%m-%d %H:%M:%S")},{current_datetime.strftime("%Y-%m-%d %H:%M:%S")}', "status": status.upper() if status else "OPEN", "page": 1, } if incident_module: params["module"] = incident_module if incident_category: params["module_category"] = incident_category if template_ids: params["template_id__in"] = template_ids incidents = paginate(client, url="/incidents", method="GET", params=params, max_results=max_results) incident_details = [] for _incident in incidents: code = _incident.get("code") if code: details = client.get_incident_detail(code) details["indicators"] = paginate(client, url=f"/incidents/{code}/indicators", method="GET") details["reference_url"] = f"https://app.brandefense.io/issues/incidents/all/{code}" incident_details.append(details) return incident_details def get_brandefense_intelligence_for_fetch(client: Client, args: dict[str, Any]) -> list[dict[str, Any]]: """Internal function to fetch intelligence for the fetch-incidents command. Args: client: Brandefense API client. args: Parameters for filtering. Returns: List of intelligence details with indicators. """ category = list_to_comma_separated_string(args.get("IntelligenceCategory")) search_term = args.get("IntelligenceSearch", "") period = int(args.get("period", 24)) max_results = int(args.get("MaxResults", DEFAULT_PAGE_SIZE)) current_datetime = datetime.now(UTC) start_datetime = current_datetime - timedelta(hours=period) params = { "page_size": DEFAULT_PAGE_SIZE, "created_at__range": f'{start_datetime.strftime("%Y-%m-%d %H:%M:%S")},{current_datetime.strftime("%Y-%m-%d %H:%M:%S")}', "page": 1, } if category: params["category__in"] = category intelligence_list = paginate(client, url="/intelligences", method="GET", params=params, max_results=max_results) # Filter by search term in tags if search_term: intelligence_list = [ item for item in intelligence_list if any(search_term.lower() in tag.lower() for tag in item.get("tags", [])) ] intelligence_details = [] for intel in intelligence_list: code = intel.get("code") if code: details = client.get_intelligence_detail(code) ind_resp = client.get_intelligence_indicators(code) if isinstance(ind_resp, list): details["indicators"] = ind_resp elif isinstance(ind_resp, dict): details["indicators"] = ind_resp.get("results", []) else: details["indicators"] = [] details["reference_url"] = f"https://app.brandefense.io/issues/intelligence/all/{code}" intelligence_details.append(details) return intelligence_details def fetch_incidents( client: Client, last_run: dict[str, Any], first_fetch_time: int | None, incident_category: list, incident_module: list, incident_status: list, fetching_issue_types: list, intelligence_category: list, intelligence_search: Any, incident_rules: list, max_results: int, ) -> tuple[dict[str, Any], list[dict]]: """Fetch incidents from Brandefense with deduplication. Uses a dual-check deduplication strategy: 1. Timestamp-based: Only process items newer than or equal to last fetch time. 2. Code-based: Track seen incident/intelligence codes in last_run to prevent duplicates when multiple items share the same created_at timestamp. The seen_codes list is stored in last_run and bounded to the last 1000 codes to prevent unlimited memory growth. Args: client: Brandefense API client. last_run: Dict containing 'last_fetch' timestamp and 'seen_codes' list. first_fetch_time: If last_run is None then fetch all incidents since this time. incident_category: Filter by incident category. incident_module: Filter by incident module. incident_status: Filter by incident status. fetching_issue_types: Types to fetch (Incident, Intelligence). intelligence_category: Filter by intelligence category. intelligence_search: Search term for intelligence filtering. incident_rules: Filter by incident rules. max_results: Maximum number of results to fetch. Returns: Tuple of (next_run dict, list of XSOAR incidents). """ MAX_SEEN_CODES = 1000 # Prevent unbounded growth of seen codes list args: dict[str, Any] = { "IncidentCategory": incident_category, "IncidentModule": incident_module, "status": incident_status, "IntelligenceCategory": intelligence_category, "IntelligenceSearch": intelligence_search, "IncidentRules": incident_rules, "MaxResults": max_results, } fetch_functions = { "Incident": get_brandefense_incidents_for_fetch, "Intelligence": get_brandefense_intelligence_for_fetch, } # Get the last fetch time and previously seen codes last_fetch = last_run.get("last_fetch", None) seen_codes: set = set(last_run.get("seen_codes", [])) if not last_fetch: args["period"] = hours_ago_from_epoch(first_fetch_time or 0) last_fetch = first_fetch_time else: last_fetch = int(last_fetch) # Compute period in hours from last_fetch so the API call filters properly args["period"] = max(1, hours_ago_from_epoch(int(last_fetch))) latest_created_time = int(last_fetch) def _parse_created_at(value: str) -> int | None: """Parse an ISO-8601 created_at string to epoch seconds, returning None on failure.""" if not value: return None try: # Handle both trailing 'Z' and offset formats normalized = value.replace("Z", "+00:00") if value.endswith("Z") else value return int(datetime.fromisoformat(normalized).timestamp()) except (ValueError, TypeError): return None # Fetch items from configured sources items: list[dict[str, Any]] = [] for fetch_type in fetching_issue_types or ["Incident"]: fetch_fn = fetch_functions.get(fetch_type) if fetch_fn: items.extend(fetch_fn(client, args)) incidents: list[dict] = [] new_seen_codes: list[str] = [] if items: # Sort by created_at (safely - items with invalid timestamps go to the end) items = sorted(items, key=lambda x: _parse_created_at(x.get("created_at", "")) or 0) for item in items: code = item.get("code", "") severity = item.get("severity", "HIGH") created_at = item.get("created_at", "") incident_created_time = _parse_created_at(created_at) if incident_created_time is None: demisto.debug(f"Skipping item with invalid created_at: {created_at}") continue # DEDUPLICATION: Skip if we've already seen this code if code in seen_codes: demisto.debug(f"Skipping duplicate item with code: {code}") continue # Only process items newer than or equal to the last fetch time if incident_created_time >= latest_created_time: # Add a fetch_type marker for the classifier fetch_type_marker = "Incident" if "module" in item else "Intelligence" item["brandefense_type"] = fetch_type_marker # Preserve original type and set classifier key if "type" in item: item["brandefense_original_type"] = item["type"] item["type"] = fetch_type_marker title = item.get("title", code) incident_xsoar = { "name": f"{title}: {code}", "occurred": timestamp_to_datestring(incident_created_time * 1000), "rawJSON": json.dumps(item), "severity": convert_to_demisto_severity(severity), } incidents.append(incident_xsoar) if code and code not in new_seen_codes: new_seen_codes.append(code) # Update latest timestamp based on ALL items (not just processed incidents) # so that we don't re-fetch already-seen items on the next run. for item in items: item_ts = _parse_created_at(item.get("created_at", "")) if item_ts and item_ts > latest_created_time: latest_created_time = item_ts # Merge seen codes: preserve order (most-recent last), bounded to MAX_SEEN_CODES. # We keep existing seen_codes order first, then append newly seen codes. all_seen_codes_list = [c for c in last_run.get("seen_codes", []) if c not in set(new_seen_codes)] + new_seen_codes all_seen_codes_list = all_seen_codes_list[-MAX_SEEN_CODES:] next_run = { "last_fetch": latest_created_time, "seen_codes": all_seen_codes_list, } demisto.debug( f"Fetched {len(incidents)} new incidents/intelligence. " f"Tracking {len(all_seen_codes_list)} seen codes. " f"Latest timestamp: {latest_created_time}" ) return next_run, incidents """ MAIN """ def main() -> None: """Main function, parses params and runs command functions.""" params = demisto.params() api_key = params.get("apikey", {}).get("password") or params.get("apikey") base_url = urljoin(params["url"], "/api/v1") verify_certificate = not params.get("insecure", False) proxy = params.get("proxy", False) demisto.debug(f"Command being called is {demisto.command()}") first_fetch_time = get_first_time_fetch(params.get("first_fetch")) reliability = params.get("integrationReliability", DBotScoreReliability.B) try: headers = {"authorization": f"Bearer {api_key}"} client = Client( base_url=base_url, verify=verify_certificate, headers=headers, proxy=proxy, ) command = demisto.command() args = demisto.args() if command == "test-module": result = test_module(client) demisto.results(result) elif command == "fetch-incidents": incident_category = params.get("IncidentCategory") incident_module = params.get("IncidentModule") incident_status = params.get("IncidentStatus") intelligence_category = params.get("IntelligenceCategory") intelligence_search = params.get("IntelligenceSearch") fetching_issue_types = params.get("FetchingIssueTypes") incident_rules = params.get("IncidentRules") # max_fetch takes precedence over the legacy MaxResults param max_results = int(params.get("max_fetch") or params.get("MaxResults") or 30) next_run, incidents = fetch_incidents( client=client, last_run=demisto.getLastRun(), first_fetch_time=first_fetch_time, incident_category=incident_category, incident_module=incident_module, incident_status=incident_status, fetching_issue_types=fetching_issue_types, intelligence_category=intelligence_category, intelligence_search=intelligence_search, incident_rules=incident_rules, max_results=max_results, ) demisto.setLastRun(next_run) demisto.incidents(incidents) elif command == "ip": args.setdefault("reliability", reliability) return_results(search_ip_command(client, args)) elif command == "domain": args.setdefault("reliability", reliability) return_results(search_domain_command(client, args)) elif command == "file": args.setdefault("reliability", reliability) return_results(search_hash_command(client, args)) elif command == "url": args.setdefault("reliability", reliability) return_results(search_url_command(client, args)) elif command == "brandefense_get_incidents": return_results(get_incidents_command(client, args)) elif command == "brandefense_get_incident_detail": return_results(get_incident_detail_command(client, args)) elif command == "brandefense_incident_indicators": return_results(get_incident_indicators_command(client, args)) elif command == "brandefense_get_incident_relatives": return_results(get_incident_relatives_command(client, args)) elif command == "brandefense_change_incident_status": return_results(change_incident_status_command(client, args)) elif command == "brandefense_get_intelligences": return_results(get_intelligence_command(client, args)) elif command == "brandefense_get_intelligence_detail": return_results(get_intelligence_detail_command(client, args)) elif command == "brandefense_intelligence_indicators": return_results(get_intelligence_indicators_command(client, args)) elif command == "brandefense_get_intelligence_rules": return_results(get_intelligence_rules_command(client, args)) elif command == "threat_search": return_results(threat_search_command(client, args)) elif command == "brandefense_get_assets": return_results(get_assets_command(client, args)) elif command == "brandefense_get_iocs": return_results(get_iocs_command(client, args)) elif command == "brandefense_get_compromised_devices": return_results(get_compromised_devices_command(client, args)) elif command == "brandefense_get_audit_logs": return_results(get_audit_logs_command(client, args)) elif command == "brandefense_get_domain_risk_assessment": return_results(get_domain_risk_assessment_command(client, args)) elif command == "brandefense_get_ioc_list": return_results(get_ioc_list_command(client, args)) elif command == "brandefense_create_confirmed_phishing": return_results(create_confirmed_phishing_command(client, args)) elif command == "brandefense_takedown_request": return_results(takedown_request_command(client, args)) elif command == "brandefense_get_indicators": return_results(get_indicators_command(client, args)) else: raise NotImplementedError(f'Command "{command}" is not implemented.') except Exception as e: demisto.error(traceback.format_exc()) return_error(f"Failed to execute {demisto.command()} command.\nError:\n{str(e)}") if __name__ in ("__main__", "__builtin__", "builtins"): main()