Cyberint Feed

Use the Cyberint Feed integration to get indicators from the feed.

Data Enrichment & Threat Intelligence · Cyberint · Feed

Details

IDCyberint Feed
ProviderCheck Point Software Technologies
CategoryData Enrichment & Threat Intelligence
From Version6.9.0
Docker Imagedemisto/python3:3.12.13.10404775
Supported ModulesAgentix XSIAM

README

Use the Cyberint Feed integration to get indicators from the feed.

Configure Cyberint Feed on Cortex XSOAR

  1. Navigate to Settings > Integrations > Servers & Services.
  2. Search for Cyberint Feed.
  3. Click Add instance to create and configure a new integration instance.

    Parameter Description Required
    Cyberint API URL Example: https://yourcompany.cyberint.io True
    API access token   True
    Fetch indicators Should be checked (true) False
    Indicator Type Which indicator types to fetch True
    Confidence Confidence about the indicator details. The value of confidence to fetch indicators from. The value between 0-100. False
    Severity Severity about the indicator details. The value of severity to fetch indicators from. The value between 0-100. False
    Tags Supports CSV values. False
    Bypass exclusion list When selected, the exclusion list is ignored for indicators from this feed. This means that if an indicator from this feed is on the exclusion list, the indicator might still be added to the system. False
    Trust any certificate (not secure)   False
    Use system proxy settings   False
  4. Click Test to validate the URLs, token, and connection.

Commands

You can execute these commands from the Cortex XSOAR CLI, as part of an automation, or in a playbook.
After you successfully execute a command, a DBot message appears in the War Room with the command details.

cyberint-get-indicators


Gets indicators from the feed.

Base Command

cyberint-get-indicators

Input

Argument Name Description Required
limit The maximum number of results to return. The default value is 10. Default is 10. Optional

Context Output

Path Type Description
Cyberint.indicator.ioc_value String The indicator value.
Cyberint.indicator.ioc_type String The indicator type.
Cyberint.indicator.description String The feed description.
Cyberint.indicator.detected_activity String The feed detected activity.
Cyberint.indicator.observation_date String The feed observation date.
Cyberint.indicator.severity_score String The feed severity score.
Cyberint.indicator.confidence String The feed confidence.

Command example

!cyberint-get-indicators limit=10 execution-timeout=700

Context Example

{
    "Cyberint": [
        {
            "fields": {
                "Description": "Recognized as Malicious.",
                "FirstSeenBySource": "2024-01-23T22:53:36+00:00",
                "reportedby": "Cyberint",
                "trafficlightprotocol": "GREEN"
            },
            "rawJSON": {
                "confidence": 80,
                "description": "Recognized as Malicious.",
                "detected_activity": "malware_payload",
                "ioc_type": "file/sha256",
                "ioc_value": "ioc1",
                "observation_date": "2024-01-23T22:53:36+00:00",
                "severity_score": 100
            },
            "service": "Cyberint",
            "type": "File",
            "value": "ioc1"
        },
        {
            "fields": {
                "Description": "Recognized as zzz.",
                "FirstSeenBySource": "2024-01-23T22:55:36+00:00",
                "reportedby": "Cyberint",
                "trafficlightprotocol": "GREEN"
            },
            "rawJSON": {
                "confidence": 80,
                "description": "Recognized as zzz.",
                "detected_activity": "malware_payload",
                "ioc_type": "file/sha256",
                "ioc_value": "ioc2",
                "observation_date": "2024-01-23T22:55:36+00:00",
                "severity_score": 100
            },
            "service": "Cyberint",
            "type": "File",
            "value": "ioc2"
        },
        {
            "fields": {
                "Description": "Recognized as xxx.",
                "FirstSeenBySource": "2024-01-23T22:53:35+00:00",
                "reportedby": "Cyberint",
                "trafficlightprotocol": "GREEN"
            },
            "rawJSON": {
                "confidence": 80,
                "description": "Recognized as xxx.",
                "detected_activity": "malware_payload",
                "ioc_type": "file/sha256",
                "ioc_value": "ioc3",
                "observation_date": "2024-01-23T22:53:35+00:00",
                "severity_score": 100
            },
            "service": "Cyberint",
            "type": "File",
            "value": "ioc3"
        },
        {
            "fields": {
                "Description": "Recognized as xxx.",
                "FirstSeenBySource": "2024-01-23T22:55:31+00:00",
                "reportedby": "Cyberint",
                "trafficlightprotocol": "GREEN"
            },
            "rawJSON": {
                "confidence": 80,
                "description": "Recognized as xxx.",
                "detected_activity": "malware_payload",
                "ioc_type": "file/sha256",
                "ioc_value": "ioc4",
                "observation_date": "2024-01-23T22:55:31+00:00",
                "severity_score": 100
            },
            "service": "Cyberint",
            "type": "File",
            "value": "ioc4"
        },
        {
            "fields": {
                "Description": "Recognized as xxx.",
                "FirstSeenBySource": "2024-01-23T22:55:35+00:00",
                "reportedby": "Cyberint",
                "trafficlightprotocol": "GREEN"
            },
            "rawJSON": {
                "confidence": 80,
                "description": "Recognized as xxx.",
                "detected_activity": "malware_payload",
                "ioc_type": "file/sha256",
                "ioc_value": "ioc5",
                "observation_date": "2024-01-23T22:55:35+00:00",
                "severity_score": 100
            },
            "service": "Cyberint",
            "type": "File",
            "value": "ioc5"
        },
        {
            "fields": {
                "Description": "Recognized as Trojan.xxx.",
                "FirstSeenBySource": "2024-01-23T22:55:39+00:00",
                "reportedby": "Cyberint",
                "trafficlightprotocol": "GREEN"
            },
            "rawJSON": {
                "confidence": 80,
                "description": "Recognized as Trojan.xxx.",
                "detected_activity": "malware_payload",
                "ioc_type": "file/sha256",
                "ioc_value": "ioc6",
                "observation_date": "2024-01-23T22:55:39+00:00",
                "severity_score": 100
            },
            "service": "Cyberint",
            "type": "File",
            "value": "ioc6"
        },
        {
            "fields": {
                "Description": "Recognized as xxx.",
                "FirstSeenBySource": "2024-01-12T01:39:06+00:00",
                "reportedby": "Cyberint",
                "trafficlightprotocol": "GREEN"
            },
            "rawJSON": {
                "confidence": 80,
                "description": "Recognized as xxx.",
                "detected_activity": "malware_payload",
                "ioc_type": "file/sha256",
                "ioc_value": "ioc7",
                "observation_date": "2024-01-12T01:39:06+00:00",
                "severity_score": 100
            },
            "service": "Cyberint",
            "type": "File",
            "value": "ioc7"
        },
        {
            "fields": {
                "Description": "Recognized as xxx.",
                "FirstSeenBySource": "2024-01-23T22:55:36+00:00",
                "reportedby": "Cyberint",
                "trafficlightprotocol": "GREEN"
            },
            "rawJSON": {
                "confidence": 80,
                "description": "Recognized as xxx.",
                "detected_activity": "malware_payload",
                "ioc_type": "file/sha256",
                "ioc_value": "ioc8",
                "observation_date": "2024-01-23T22:55:36+00:00",
                "severity_score": 100
            },
            "service": "Cyberint",
            "type": "File",
            "value": "ioc8"
        },
        {
            "fields": {
                "Description": "Recognized as xxx.",
                "FirstSeenBySource": "2023-12-16T21:28:01+00:00",
                "reportedby": "Cyberint",
                "trafficlightprotocol": "GREEN"
            },
            "rawJSON": {
                "confidence": 70,
                "description": "Recognized as xxx.",
                "detected_activity": "malware_payload",
                "ioc_type": "file/sha256",
                "ioc_value": "ioc9",
                "observation_date": "2023-12-16T21:28:01+00:00",
                "severity_score": 100
            },
            "service": "Cyberint",
            "type": "File",
            "value": "ioc9"
        },
        {
            "fields": {
                "Description": "Recognized as xxx.",
                "FirstSeenBySource": "2024-01-23T22:55:35+00:00",
                "reportedby": "Cyberint",
                "trafficlightprotocol": "GREEN"
            },
            "rawJSON": {
                "confidence": 80,
                "description": "Recognized as xxx.",
                "detected_activity": "malware_payload",
                "ioc_type": "file/sha256",
                "ioc_value": "ioc10",
                "observation_date": "2024-01-23T22:55:35+00:00",
                "severity_score": 100
            },
            "service": "Cyberint",
            "type": "File",
            "value": "ioc10"
        }
    ]
}

Human Readable Output

Indicators from Cyberint Feed
Name Type Description
Detected activity String Type of detected activity.
IoC type String The indicator type.
IoC value String The indicator value.
Observation date String Observation date of detected activity.
Severity score Number Severity score of detected activity.
Confidence Number Confidence of detected activity.
Description String Description of detected activity.

cyberint-get-file-sha256


Gets File SHA256 from the feed.

Base Command

cyberint-get-file-sha256

Input

Argument Name Description Required
value File SHA256 hash Required

Context Output

Path Type Description
Cyberint.file_sha256.entity.type String The indicator type.
Cyberint.file_sha256.entity.value String The indicator value.
Cyberint.file_sha256.enrichment.first_seen String First seen.
Cyberint.file_sha256.enrichment.download_urls String Download URLs.
Cyberint.file_sha256.enrichment.filenames String Filenames.
Cyberint.file_sha256.benign String Benign.
Cyberint.file_sha256.risk.malicious_score String Malicious score.
Cyberint.file_sha256.risk.occurrences_count String Occurrences count.

Command example

!cyberint-get-file-sha256 value=6a7b02c43837dcb8e40d271edb88d13d2e723c721a74931857aaef4853317789

Context Example

{
    "data": {
        "entity": {
            "type": "file/sha256",
            "value": "6a7b02c43837dcb8e40d271edb88d13d2e723c721a74931857aaef4853317789"
        },
        "risk": {
            "malicious_score": 100,
            "detected_activities": [
                {
                    "type": "malware",
                    "observation_date": "2025-03-05T14:47:43.994848+00:00",
                    "description": "",
                    "confidence": 100,
                    "occurrences_count": 1
                },
                {
                    "type": "malware_payload",
                    "observation_date": "2025-02-12T21:08:13+00:00",
                    "description": "Detected in 1 source(s). Recognized as Trojan.Agent.CYZT.",
                    "confidence": 80,
                    "occurrences_count": 1
                }
            ],
            "occurrences_count": 2
        },
        "enrichment": {
            "related_entities": null,
            "filenames": [
                "rifaien2-TwxvxoHtj44icOI0.exe"
            ],
            "first_seen": "2025-02-12T21:08:13+00:00",
            "download_urls": []
        },
        "benign": false
    }
}

Human Readable Output

File SHA256 Entity
Name Type Description
Type String The indicator type.
Value String The indicator value.
Malicious score Number Malicious score.
Benign Boolean Benign.
File SHA256 Enrichment
Name Type Description
Filenames String List of filenames.
First seen String First seen.
Download URLs String List of download URLs.
File SHA256 Detected activities
Name Type Description
Type String Type of detected activity.
Observation date String Observation date of detected activity.
Description String Description of detected activity.
Confidence Number Confidence of detected activity.
Occurrences count Number Occurrences count of detected activity.

cyberint-get-domain


Gets Domain from the feed.

Base Command

cyberint-get-domain

Input

Argument Name Description Required
value Domain Required

Context Output

Path Type Description
Cyberint.domain.entity.type String The indicator type.
Cyberint.domain.entity.value String The indicator value.
Cyberint.domain.risk.malicious_score String Malicious score.
Cyberint.domain.risk.occurrences_count String Occurrences count.
Cyberint.domain.enrichment.ips String IPs.
Cyberint.domain.enrichment.whois.created_date String Created date.
Cyberint.domain.enrichment.whois.updated_date String Updated date.
Cyberint.domain.enrichment.whois.expiration_date String Expiration date.
Cyberint.domain.enrichment.whois.registrant_name String Registrant name.
Cyberint.domain.enrichment.whois.registrant_email String Registrant email.
Cyberint.domain.enrichment.whois.registrant_organization String Registrant organization.
Cyberint.domain.enrichment.whois.registrant_country String Registrant country.
Cyberint.domain.enrichment.whois.registrant_telephone String Registrant telephone.
Cyberint.domain.enrichment.whois.technical_contact_email String Technical contact email.
Cyberint.domain.enrichment.whois.technical_contact_name String Technical contact name.
Cyberint.domain.enrichment.whois.technical_contact_organization String Technical contact organization.
Cyberint.domain.enrichment.whois.registrar_name String Registrar name.
Cyberint.domain.enrichment.whois.admin_contact_name String Admin contact name.
Cyberint.domain.enrichment.whois.admin_contact_organization String Admin contact organization.
Cyberint.domain.enrichment.whois.admin_contact_email String Admin contact email.
Cyberint.domain.benign String Benign.

Command example

!cyberint-get-domain value=dummy.com

Context Example

{
    "data": {
        "entity": {
            "type": "domain",
            "value": "domain.com"
        },
        "risk": {
            "malicious_score": 80,
            "detected_activities": [
                {
                    "type": "infecting_url",
                    "observation_date": "2025-03-05T14:47:23.534044+00:00",
                    "description": "URL that may infect it’s visitors with malware.",
                    "confidence": 100,
                    "occurrences_count": 1
                },
                {
                    "type": "phishing_website",
                    "observation_date": "2024-09-16T06:26:16+00:00",
                    "description": "Detected phishing website targeting Dummy.",
                    "confidence": 20,
                    "occurrences_count": 1
                }
            ],
            "occurrences_count": 2
        },
        "enrichment": {
            "related_entities": null,
            "ips": [
                "11.197.130.221"
            ],
            "whois": {
                "registrant_name": null,
                "registrant_email": null,
                "registrant_organization": null,
                "registrant_country": "USA",
                "registrant_telephone": null,
                "technical_contact_email": null,
                "technical_contact_name": null,
                "technical_contact_organization": null,
                "registrar_name": "Registrar.com",
                "admin_contact_name": null,
                "admin_contact_organization": null,
                "admin_contact_email": null,
                "created_date": "2024-09-10T09:29:58",
                "updated_date": "2024-10-18T05:44:51",
                "expiration_date": "2025-09-10T23:59:59"
            }
        },
        "benign": false
    }
}

Human Readable Output

Domain Entity
Name Type Description
Type String The indicator type.
Value String The indicator value.
Malicious score Number Malicious score.
Occurrences count Number Occurrences count.
Benign Boolean Benign.
Domain Enrichment
Name Type Description
IPs String List of IP addresses.
Whois registrant name String Whois registrant name.
Whois registrant email String Whois registrant email.
Whois registrant organization String Whois registrant organization.
Whois registrant country String Whois registrant country.
Whois registrant telephone String Whois registrant telephone.
Whois technical contact email String Whois technical contact email.
Whois technical contact name String Whois technical contact name.
Whois technical contact organization String Whois technical contact organization.
Whois registrar name String Whois registrar name.
Whois admin contact name String Whois admin contact name.
Whois admin contact organization String Whois admin contact organization.
Whois admin contact email String Whois admin contact email.
Whois admin contact email String Whois admin contact email.
Created date String Created date.
Updated date String Updated date.
Expiration date String Expiration date.
Domain Detected activities
Name Type Description
Type String Type of detected activity.
Observation date String Observation date of detected activity.
Description String Description of detected activity.
Confidence Number Confidence of detected activity.
Occurrences count Number Occurrences count of detected activity.

cyberint-get-ipv4


Gets Domain from the feed.

Base Command

cyberint-get-ipv4

Input

Argument Name Description Required
value IPv4 Required

Context Output

Path Type Description
Cyberint.ipv4.entity.type String The indicator type.
Cyberint.ipv4.entity.value String The indicator value.
Cyberint.ipv4.risk.malicious_score String Malicious score.
Cyberint.ipv4.risk.occurrences_count String Occurrences count.
Cyberint.ipv4.enrichment.geo.country String Country.
Cyberint.ipv4.enrichment.geo.city String City.
Cyberint.ipv4.enrichment.asn.number String ASN number.
Cyberint.ipv4.enrichment.asn.organization String ASN organization.
Cyberint.ipv4.enrichment.suspicious_urls String Suspicious URLs.
Cyberint.ipv4.enrichment.suspicious_domains String Suspicious domains.
Cyberint.ipv4.benign String Benign.

Command example

!cyberint-get-ipv4 value=1.1.1.1

Context Example

{
    "data": {
        "entity": {
            "type": "ipv4",
            "value": "11.197.130.221"
        },
        "risk": {
            "malicious_score": 100,
            "detected_activities": [
                {
                    "type": "payload_delivery",
                    "observation_date": "2025-02-13T08:38:50+00:00",
                    "description": "Detected hosting malware.",
                    "confidence": 20,
                    "occurrences_count": 836
                },
                {
                    "type": "phishing_website",
                    "observation_date": "2025-03-05T10:17:32+00:00",
                    "description": "Detected phishing website targeting Dummy, ING Direct, genericcloudflare.",
                    "confidence": 20,
                    "occurrences_count": 143
                },
                {
                    "type": "cnc_server",
                    "observation_date": "2025-02-14T22:21:12.084000+00:00",
                    "description": "Detected in 21 source(s). Recognized as Quasar RAT. Detected activity linked to: Bumblebee (Malware), Cotton Sandstorm (Threat-Actor-Group), DadSec (Malware), GHOSTSPIDER (Malware), Quasar RAT (Malware), Salt Typhoon (Threat-Actor-Group), Sneaky 2FA (Malware), Vidar (Malware)",
                    "confidence": 90,
                    "occurrences_count": 21
                }
            ],
            "occurrences_count": 1000
        },
        "enrichment": {
            "related_entities": [
                {
                    "entity_id": "c654837d-444e-4f5c-a444-09fd8250696c",
                    "entity_type": "Malware",
                    "entity_name": "GHOSTSPIDER"
                },
                {
                    "entity_id": "70b54325-05ea-46c6-b4e9-b25bc3617104",
                    "entity_type": "Threat-Actor-Group",
                    "entity_name": "Salt Typhoon"
                },
                {
                    "entity_id": "baffd4c4-4483-4b84-96eb-0d19af94d2e8",
                    "entity_type": "Malware",
                    "entity_name": "DadSec"
                },
                {
                    "entity_id": "862341a5-1951-4e09-b3c1-baac41dc7bcb",
                    "entity_type": "Malware",
                    "entity_name": "Vidar"
                },
                {
                    "entity_id": "7b0a986f-733e-4497-8867-6aed00b802b8",
                    "entity_type": "Threat-Actor-Group",
                    "entity_name": "Cotton Sandstorm"
                },
                {
                    "entity_id": "58cbb47d-176d-4937-9ebc-5121ceb36cf9",
                    "entity_type": "Malware",
                    "entity_name": "Sneaky 2FA"
                },
                {
                    "entity_id": "2728ad3e-d870-4654-afd3-9a839f97dd72",
                    "entity_type": "Malware",
                    "entity_name": "Bumblebee"
                },
                {
                    "entity_id": "fc26b8a7-a7cc-47b8-be1e-92b7a969543b",
                    "entity_type": "Malware",
                    "entity_name": "Quasar RAT"
                }
            ],
            "geo": {
                "country": "United States",
                "city": null
            },
            "asn": {
                "number": 16509,
                "organization": "AMAZON-02"
            },
            "suspicious_urls": [],
            "suspicious_domains": []
        },
        "benign": false
    }
}

Human Readable Output

IPv4 Entity
Name Type Description
Type String The indicator type.
Value String The indicator value.
Malicious score Number Malicious score.
Occurrences count Number Occurrences count.
IPs String List of IP addresses.
Hostname String Hostname.
Domain String Domain.
Benign Boolean Benign.
IPv4 Enrichment
Name Type Description
Suspicious Urls String List of Suspicious Urls.
Suspicious Domains String List of Suspicious domains.
IPv4 Detected activities
Name Type Description
Type String Type of detected activity.
Observation date String Observation date of detected activity.
Description String Description of detected activity.
Confidence Number Confidence of detected activity.
Occurrences count Number Occurrences count of detected activity.

cyberint-get-url


Gets Domain from the feed.

Base Command

cyberint-get-url

Input

Argument Name Description Required
value URL Required

Context Output

Path Type Description
Cyberint.url.entity.type String The indicator type.
Cyberint.url.entity.value String The indicator value.
Cyberint.url.risk.malicious_score String Malicious score.
Cyberint.url.risk.occurrences_count String Occurrences count.
Cyberint.url.enrichment.ips String IPs.
Cyberint.url.enrichment.hostname String Hostname.
Cyberint.url.enrichment.domain String Domain.
Cyberint.url.benign String Benign.

Command example

!cyberint-get-url value=http://dummy.com

Context Example

{
    "data": {
        "entity": {
            "type": "url",
            "value": "http://dummy.com"
        },
        "risk": {
            "malicious_score": 80,
            "detected_activities": [
                {
                    "type": "infecting_url",
                    "observation_date": "2025-03-05T11:18:01.941280+00:00",
                    "description": "URL that may infect it’s visitors with malware.",
                    "confidence": 100,
                    "occurrences_count": 1
                }
            ],
            "occurrences_count": 1
        },
        "enrichment": {
            "related_entities": null,
            "ips": [],
            "hostname": null,
            "domain": null
        },
        "benign": false
    }
}

Human Readable Output

URL Entity
Name Type Description
Type String The indicator type.
Value String The indicator value.
Malicious score Number Malicious score.
Occurrences count Number Occurrences count.
IPs String List of IP addresses.
Hostname String Hostname.
Domain String Domain.
Benign Boolean Benign.
URL Detected activities
Name Type Description
Type String Type of detected activity.
Observation date String Observation date of detected activity.
Description String Description of detected activity.
Confidence Number Confidence of detected activity.
Occurrences count Number Occurrences count of detected activity.

cyberint-cve-enrich


Enriches one or more CVE identifiers with Cyberint vulnerability intelligence, including EPSS, CVSS, CWE, the Cyberint CVE score and active-exploitation evidence.

Base Command

cyberint-cve-enrich

Input

Argument Name Description Required
cve_id CVE identifier to enrich (e.g., CVE-2024-1234). Supports a comma-separated list of identifiers. Required

Context Output

Path Type Description
Cyberint.CVE.cve_id String The CVE identifier.
Cyberint.CVE.cyberint_score Number Cyberint proprietary risk score for the CVE.
Cyberint.CVE.epss Number EPSS (Exploit Prediction Scoring System) probability score.
Cyberint.CVE.description String The CVE description.
Cyberint.CVE.published Date The date the CVE was published.
Cyberint.CVE.last_updated Date The date the CVE was last updated.
Cyberint.CVE.cvss.cvss_v3.base_score Number CVSS v3 base score.
Cyberint.CVE.cvss.cvss_v3.base_severity String CVSS v3 base severity.
Cyberint.CVE.cvss.cvss_v4.base_score Number CVSS v4 base score.
Cyberint.CVE.cwes.cwe_id String Associated CWE identifier.
Cyberint.CVE.cwes.name String Associated CWE name.
Cyberint.CVE.risk_factors Unknown Risk factors associated with the CVE.
Cyberint.CVE.exploited_by Unknown Threat actors observed exploiting the CVE.
Cyberint.CVE.products Unknown Products affected by the CVE.
Cyberint.CVE.recommendation String Cyberint remediation recommendation for the CVE.
Cyberint.CVE.references Unknown External references for the CVE.
CVE.ID String The CVE identifier.
CVE.CVSS String The CVSS score of the CVE.
CVE.Published Date The date the CVE was published.
CVE.Description String The CVE description.

Command Example

!cyberint-cve-enrich cve_id="CVE-2024-1234"

cyberint-credential-leak-lookup


Looks up leaked employee or customer credentials associated with a company domain, using the Cyberint leaked-credentials API.

Base Command

cyberint-credential-leak-lookup

Input

Argument Name Description Required
domain The company domain to search leaked credentials for (e.g., example.com). Required
last_seen_from Only return credentials last seen on or after this ISO-formatted date (YYYY-MM-DDTHH:MM:SSZ). Optional
last_seen_to Only return credentials last seen on or before this ISO-formatted date (YYYY-MM-DDTHH:MM:SSZ). Optional
limit Maximum number of leaked credential records to return per credential type (employee/customer). Optional

Context Output

Path Type Description
Cyberint.LeakedCredential.domain String The company domain that was searched.
Cyberint.LeakedCredential.total Number Total number of leaked credential records returned.
Cyberint.LeakedCredential.employee_total Number Number of leaked employee credential records returned.
Cyberint.LeakedCredential.customer_total Number Number of leaked customer credential records returned.
Cyberint.LeakedCredential.employee_credentials.username String The leaked employee account username.
Cyberint.LeakedCredential.employee_credentials.first_seen Date The date the employee credential was first observed.
Cyberint.LeakedCredential.employee_credentials.last_seen Date The date the employee credential was last observed.
Cyberint.LeakedCredential.employee_credentials.entries Unknown The leaked credential entries (password, source, URL) for the employee account.
Cyberint.LeakedCredential.customer_credentials.username String The leaked customer account username.
Cyberint.LeakedCredential.customer_credentials.first_seen Date The date the customer credential was first observed.
Cyberint.LeakedCredential.customer_credentials.last_seen Date The date the customer credential was last observed.
Cyberint.LeakedCredential.customer_credentials.entries Unknown The leaked credential entries (password, source, URL) for the customer account.

Command Example

!cyberint-credential-leak-lookup domain="example.com"

Configuration parameters

  • url — Cyberint API URL (required)
  • client_name — Company Name (required)
  • access_token — API Key (Leave empty. Fill in the API Key in the password field.) (required)
  • feed — Fetch indicators
  • feedReputation — Indicator Reputation
  • feedReliability — Source Reliability (required)
  • tlp_color — Traffic Light Protocol Color
  • indicator_type — Indicator Type (required)
  • feed_name — Feed Name (required)
  • confidence_from — Confidence
  • severity_from — Severity
  • feedExpirationPolicy
  • feedExpirationInterval
  • feedFetchInterval — Feed Fetch Interval
  • feedTags — Tags
  • feedBypassExclusionList — Bypass exclusion list
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings

Commands (7)

  • cyberint-credential-leak-lookup

    Looks up leaked employee or customer credentials associated with a company domain, using the Cyberint leaked-credentials API.

  • cyberint-cve-enrich

    Enriches one or more CVE identifiers with Cyberint vulnerability intelligence, including EPSS, CVSS, CWE, the Cyberint CVE score and active-exploitation evidence.

  • cyberint-get-domain

    Gets Domain enrichment from the feed.

  • cyberint-get-file-sha256

    Gets File SHA256 enrichment from the feed.

  • cyberint-get-indicators

    Gets indicators from the feed.

  • cyberint-get-ipv4

    Gets IPv4 enrichment from the feed.

  • cyberint-get-url

    Gets URL enrichment from the feed.

import http
import json
import math
from json import JSONDecodeError
from typing import Any

import demistomock as demisto
import urllib3
from CommonServerPython import *

urllib3.disable_warnings()

DATE_FORMAT = "%Y-%m-%d"
DEFAULT_INTERVAL = 30  # 30 minutes
EXECUTION_TIMEOUT_SECONDS = 1200  # 20 minutes
MAX_LIMIT_SIZE_PER_EXEC = 100000
PAGE_SIZE = 20000


class Client(BaseClient):
    """
    Client to use in the Cyberint Feed integration.
    """

    def __init__(
        self,
        base_url: str,
        access_token: str,
        verify: bool = False,
        proxy: bool = False,
    ):
        params = demisto.params()
        self._cookies = {"access_token": access_token}
        self._headers = {
            "X-Integration-Type": "XSOAR",
            "X-Integration-Instance-Name": demisto.integrationInstance(),
            "X-Integration-Instance-Id": "",
            "X-Integration-Customer-Name": params.get("client_name", ""),
            "X-Integration-Version": str(get_pack_version()),
        }
        super().__init__(base_url, verify=verify, proxy=proxy, headers=self._headers)

    @logger
    def request_daily_feed(
        self, date_time: str = None, limit: int = 1000, execution_start_time: datetime = datetime.now(), test: bool = False
    ) -> list[dict[str, Any]]:
        """
        Retrieves all entries from the feed with pagination support.

        Args:
            date_time (str): The date-time value to use in the URL. Defaults to None.
            limit (int): The maximum number of entries to retrieve per request. Defaults to 1000.
            execution_start_time (datetime): The start time of the execution. Defaults to now.
            test (bool): If true, only return one page for testing connection.

        Returns:
            A list of objects, containing the indicators.
        """
        result = []
        init_offset = offset = demisto.getIntegrationContext().get("offset", 0)
        date_time = date_time or str(datetime.now().strftime(DATE_FORMAT))
        has_more = True

        demisto.debug(f"Fetching feed offset {offset}")

        while has_more:
            if offset >= init_offset + MAX_LIMIT_SIZE_PER_EXEC:
                demisto.setIntegrationContext({"date_time": date_time, "offset": offset})
                has_more = False
                continue

            demisto.debug(f"Fetching feed offset {offset}")

            # if the execution exceeded the timeout we will break
            if not test and is_execution_time_exceeded(start_time=execution_start_time):
                demisto.debug(f"Execution time exceeded: {EXECUTION_TIMEOUT_SECONDS} seconds from: {execution_start_time}")
                has_more = False
                continue

            start_time = time.time()

            # Using the method to fetch and process the feed response
            indicators = self.process_feed_response(date_time, limit, offset)

            if not indicators:
                # No more data or an error occurred
                demisto.setIntegrationContext({"offset": 0})
                has_more = False
            else:
                result.extend(indicators)  # Append valid indicators to the result

                end_time = time.time()
                demisto.debug(f"Duration of offset processing {offset}: {math.ceil(end_time - start_time)} seconds")
                # Update the offset for the next request
                offset += limit
                has_more = True
                demisto.debug(f"Feed has more incidents for fetching: {has_more}")

            if test:  # if test module, end the loop
                demisto.debug("Test execution")
                has_more = False
                continue

        return result

    def process_feed_response(self, date_time: str, limit: int, offset: int) -> list[dict[str, Any]]:
        """
        Makes the API request to retrieve the indicators, handles JSON decoding, and processes the feed.

        Args:
            date_time (str): The date-time value to use in the URL.
            limit (int): The maximum number of entries to retrieve per request.
            offset (int): The offset to retrieve the correct page of results.

        Returns:
            A list of indicator dictionaries, or an empty list if no valid indicators are found.
        """
        result: List[Any] = []
        response = self.retrieve_indicators_from_api(date_time, limit, offset)

        try:
            # If json is invalid, return empty list (non 200 or invalid JSON)
            feeds = response.strip().split("\n")
            ioc_feeds = [json.loads(feed) for feed in feeds]
        except JSONDecodeError as e:
            demisto.error(f"Failed to decode JSON: {e}")
            return result  # Return empty result on failure

        if not ioc_feeds:  # No data found
            demisto.debug("No more indicators found")
            return result

        # Process valid feeds
        for indicator in ioc_feeds:
            ioc_value = indicator.get("ioc_value")
            if auto_detect_indicator_type(ioc_value):
                result.append(indicator)

        return result

    @logger
    def retrieve_indicators_from_api(self, date_time, limit, offset):
        url_suffix = f"/ioc/api/v1/feed/daily/{date_time}?limit={limit}&offset={offset}"
        demisto.debug(f"URL to fetch indicators: {url_suffix}")
        response = self._http_request(
            method="GET",
            url_suffix=url_suffix,
            cookies=self._cookies,
            resp_type="text",
            timeout=120,
            retries=3,
        )
        return response

    @logger
    def retrieve_file_sha256_from_api(self, value: str) -> dict[str, Any]:
        """
        Makes the API request to retrieve File SHA256 IoC.

        Args:
            value (str): string ^[a-f0-9]{64}$.

        Returns:
            Enrichment information.
        """
        url_suffix = f"/ioc/api/v1/file/sha256?value={value}"
        demisto.debug(f"URL to retrieve File SHA256 IoC: {url_suffix}")
        response = self._http_request(
            method="GET",
            url_suffix=url_suffix,
            cookies=self._cookies,
            timeout=120,
            retries=3,
        )
        return response

    @logger
    def retrieve_domain_from_api(self, value: str) -> dict[str, Any]:
        """
        Makes the API request to retrieve Domain IoC.

        Args:
            value (str): string ^[a-f0-9]{64}$.

        Returns:
            Enrichment information.
        """
        url_suffix = f"/ioc/api/v1/domain?value={value}"
        demisto.debug(f"URL to retrieve Domain IoC: {url_suffix}")
        response = self._http_request(
            method="GET",
            url_suffix=url_suffix,
            cookies=self._cookies,
            timeout=120,
            retries=3,
        )
        return response

    @logger
    def retrieve_ipv4_from_api(self, value: str) -> dict[str, Any]:
        """
        Makes the API request to retrieve IPv4 IoC.

        Args:
            value (str): string ^[a-f0-9]{64}$.

        Returns:
            Enrichment information.
        """
        url_suffix = f"/ioc/api/v1/ipv4?value={value}"
        demisto.debug(f"URL to retrieve IPv4 IoC: {url_suffix}")
        response = self._http_request(
            method="GET",
            url_suffix=url_suffix,
            cookies=self._cookies,
            timeout=120,
            retries=3,
        )
        return response

    @logger
    def retrieve_url_from_api(self, value: str) -> dict[str, Any]:
        """
        Makes the API request to retrieve URL IoC.

        Args:
            value (str): string.

        Returns:
            Enrichment information.
        """
        url_suffix = f"/ioc/api/v1/url?value={value}"
        demisto.debug(f"URL to retrieve URL IoC: {url_suffix}")
        response = self._http_request(
            method="GET",
            url_suffix=url_suffix,
            cookies=self._cookies,
            timeout=120,
            retries=3,
        )
        return response

    @logger
    def retrieve_cve_intelligence(self, cve_id: str) -> dict[str, Any]:
        """
        Makes the API request to retrieve enriched CVE intelligence.

        Args:
            cve_id (str): The CVE identifier (e.g. CVE-2024-1234).

        Returns:
            CVE intelligence information (a VulnerabilityDetail object).
        """
        url_suffix = f"/cve-intel/external/api/v1/vulnerability/{cve_id}"
        demisto.debug(f"URL to retrieve CVE intelligence: {url_suffix}")
        return self._http_request(
            method="GET",
            url_suffix=url_suffix,
            cookies=self._cookies,
            timeout=120,
            retries=3,
        )

    @logger
    def retrieve_leaked_credentials_by_domain(self, domain: str) -> dict[str, Any]:
        """
        Makes the API request to retrieve leaked credentials for a company domain.

        Args:
            domain (str): The company domain to search leaked credentials for.

        Returns:
            Leaked credentials information (a Response[ByDomainData] object).
        """
        url_suffix = "/exposed-credentials/by_domain/"
        demisto.debug(f"URL to retrieve leaked credentials: {url_suffix}")
        return self._http_request(
            method="POST",
            url_suffix=url_suffix,
            json_data={"domain": domain},
            cookies=self._cookies,
            timeout=120,
            retries=3,
        )


def test_module(client: Client, feed_enabled: Optional[bool]) -> str:
    """
    Builds the iterator to check that the feed is accessible.

    Args:
        client: Client object.

    Returns:
        Outputs.
    """

    try:
        if feed_enabled:
            demisto.info("Feed is enabled. Check that the feed is accessible.")
            client.request_daily_feed(limit=10, test=True)
        else:
            demisto.info("Feed is disabled. Check that the enrichment is accessible.")
            client.retrieve_domain_from_api("checkpoint.com")
    except DemistoException as exc:
        if exc.res and (exc.res.status_code == http.HTTPStatus.UNAUTHORIZED or exc.res.status_code == http.HTTPStatus.FORBIDDEN):
            return "Authorization Error: invalid `API Token`"

        raise exc

    return "ok"


def fetch_indicators(
    client: Client,
    tlp_color: str,
    feed_names: list[str],
    indicator_types: list[str],
    confidence_from: int,
    severity_from: int,
    date_time: str = None,
    feed_tags: List = [],
    limit: int = -1,
    execution_start_time: datetime = datetime.now(),
) -> list[dict[str, Any]]:
    """
    Retrieves indicators from the feed.

    Args:
        client (Client): API Client.
        tlp_color (str):  The Traffic Light Protocol (TLP) designation to apply to indicators fetched from the feed.
        feed_names (list): The feed names.
        indicator_types (list): Which indicator types to fetch.
        confidence_from (int): The value of confidence to fetch indicators from.
        severity_from (int): The value of severity to fetch indicators from.
        date_time (str): Date time string to fetch indicators from.
        feed_tags (list): tags to assign fetched indicators.
        limit (int): The maximum number of results to return.
        execution_start_time (datetime): The start time of the execution. Defaults to now.

    Returns:
        Indicators.
    """
    demisto.debug("Fetching indicators")
    iterator = client.request_daily_feed(date_time, limit=PAGE_SIZE, execution_start_time=execution_start_time)
    indicators = []

    for item in iterator:
        if (
            ("All" in indicator_types or item.get("ioc_type") in indicator_types)
            and ("All" in feed_names or item.get("detected_activity") in feed_names)
            and (item.get("confidence") >= confidence_from)
            and (item.get("severity_score") >= severity_from)
        ):
            indicator_value = item["ioc_value"]
            if indicator_type := auto_detect_indicator_type(indicator_value):
                indicator_obj = {
                    "type": indicator_type,
                    "value": indicator_value,
                    "service": "Cyberint",
                    "rawJSON": item,
                    "fields": {
                        "reportedby": "Cyberint",
                        "firstseenbysource": item.get("observation_date"),
                        "detected_activity": item.get("detected_activity"),
                        "severity_score": item.get("severity_score"),
                        "confidence": item.get("confidence"),
                        "description": item.get("description"),
                    },
                }

                if feed_tags:
                    indicator_obj["fields"]["tags"] = feed_tags

                if tlp_color:
                    indicator_obj["fields"]["trafficlightprotocol"] = tlp_color

                indicators.append(indicator_obj)

        if limit > 0 and len(indicators) >= limit:
            demisto.debug(f"Indicators limit reached (total): {len(indicators)}")
            break

    return indicators


def get_indicators_command(
    client: Client,
    args: dict[str, Any],
) -> CommandResults:
    """
    Wrapper for retrieving indicators from the feed to the war-room.

    Args:
        client: Cyberint API Client.
        args: Command arguments.

    Returns:
        Outputs indicators.
    """

    date = args.get("date") or datetime.now().strftime("%Y-%m-%d")
    limit = int(args.get("limit", 0))
    offset = int(args.get("offset", 0))

    indicators = client.process_feed_response(date, limit, offset)

    human_readable = tableToMarkdown(
        "Indicators from Cyberint Feed:",
        indicators,
        headers=["detected_activity", "ioc_type", "ioc_value", "observation_date", "severity_score", "confidence", "description"],
        headerTransform=ioc_header_transformer,
        removeNull=False,
    )

    return CommandResults(
        readable_output=human_readable,
        outputs_prefix="Cyberint.indicator",
        outputs_key_field="value",
        raw_response=indicators,
        outputs=indicators,
    )


def get_url_command(
    client: Client,
    args: dict[str, Any],
) -> CommandResults:
    """
    Wrapper for retrieving enrichment for URL from the feed to the war-room.

    Args:
        client: Cyberint API Client.
        args: Command arguments.

    Returns:
        Outputs indicators.
    """

    value = args.get("value", "")

    indicator = client.retrieve_url_from_api(value)

    indicator_data = indicator.get("data", {})
    indicator_formatted = [
        {
            "type": indicator_data.get("entity", {}).get("type"),
            "value": indicator_data.get("entity", {}).get("value"),
            "malicious_score": (indicator_data.get("risk") or {}).get("malicious_score"),
            "occurrences_count": (indicator_data.get("risk") or {}).get("occurrences_count"),
            "ips": (indicator_data.get("enrichment") or {}).get("ips"),
            "hostname": (indicator_data.get("enrichment") or {}).get("hostname"),
            "domain": (indicator_data.get("enrichment") or {}).get("domain"),
            "benign": indicator_data.get("benign"),
        }
    ]

    human_readable = tableToMarkdown(
        "URL Entity",
        indicator_formatted,
        headers=["type", "value", "malicious_score", "occurrences_count", "ips", "hostname", "domain", "benign"],
        headerTransform=indicator_header_transformer,
        removeNull=False,
    )

    detected_activities: list = (indicator_data.get("risk", {}) or {}).get("detected_activities", [])

    for activity in detected_activities:
        activity = activity or {}
        activities_formatted = [
            {
                "type": activity.get("type", ""),
                "observation_date": activity.get("observation_date", ""),
                "description": activity.get("description", ""),
                "confidence": activity.get("confidence", ""),
                "occurrences_count": activity.get("occurrences_count", ""),
            }
        ]

        human_readable += tableToMarkdown(
            "URL Detected activities",
            activities_formatted,
            date_fields=["observation_date"],
            headers=["type", "observation_date", "description", "confidence", "occurrences_count"],
            headerTransform=indicator_header_transformer,
            removeNull=False,
        )

    related_entities: list = (indicator_data.get("enrichment", {}) or {}).get("related_entities", []) or []

    for entity in related_entities:
        entity = entity or {}
        entities_formatted = [
            {
                "entity_id": entity.get("entity_id", ""),
                "entity_type": entity.get("entity_type", ""),
                "entity_name": entity.get("entity_name", ""),
            }
        ]

        human_readable += tableToMarkdown(
            "URL Related Entities",
            entities_formatted,
            headers=["entity_id", "entity_type", "entity_name"],
            headerTransform=indicator_header_transformer,
            removeNull=False,
        )

    return CommandResults(
        readable_output=human_readable,
        outputs_prefix="Cyberint.url",
        outputs_key_field="value",
        raw_response=indicator_data,
        outputs=indicator_data,
    )


def get_ipv4_command(
    client: Client,
    args: dict[str, Any],
) -> CommandResults:
    """
    Wrapper for retrieving enrichment for Domain from the feed to the war-room.

    Args:
        client: Cyberint API Client.
        args: Command arguments.

    Returns:
        Outputs indicators.
    """

    value = args.get("value", "")

    indicator = client.retrieve_ipv4_from_api(value)

    indicator_data = indicator.get("data", {})
    indicator_formatted = [
        {
            "type": indicator_data.get("entity", {}).get("type"),
            "value": indicator_data.get("entity", {}).get("value"),
            "malicious_score": (indicator_data.get("risk") or {}).get("malicious_score"),
            "occurrences_count": (indicator_data.get("risk") or {}).get("occurrences_count"),
            "country": (indicator_data.get("enrichment", {}).get("geo") or {}).get("country"),
            "city": (indicator_data.get("enrichment", {}).get("geo") or {}).get("city"),
            "asn_number": (indicator_data.get("enrichment", {}).get("asn") or {}).get("number"),
            "asn_organization": (indicator_data.get("enrichment", {}).get("asn") or {}).get("organization"),
            "suspicious_urls": indicator_data.get("enrichment", {}).get("suspicious_urls", []),
            "suspicious_domains": indicator_data.get("enrichment", {}).get("suspicious_domains", []),
            "benign": indicator_data.get("benign"),
        }
    ]

    human_readable = tableToMarkdown(
        "IPv4 Entity",
        indicator_formatted,
        headers=[
            "type",
            "value",
            "malicious_score",
            "occurrences_count",
            "country",
            "city",
            "asn_number",
            "asn_organization",
            "benign",
        ],
        headerTransform=indicator_header_transformer,
        removeNull=False,
    )

    human_readable += tableToMarkdown(
        "IPv4 Enrichment",
        indicator_formatted,
        headers=["suspicious_urls", "suspicious_domains"],
        headerTransform=indicator_header_transformer,
        removeNull=False,
    )

    detected_activities: list = (indicator_data.get("risk", {}) or {}).get("detected_activities", [])

    for activity in detected_activities:
        activity = activity or {}
        activities_formatted = [
            {
                "type": activity.get("type", ""),
                "observation_date": activity.get("observation_date", ""),
                "description": activity.get("description", ""),
                "confidence": activity.get("confidence", ""),
                "occurrences_count": activity.get("occurrences_count", ""),
            }
        ]

        human_readable += tableToMarkdown(
            "IPv4 Detected activities",
            activities_formatted,
            date_fields=["observation_date"],
            headers=["type", "observation_date", "description", "confidence", "occurrences_count"],
            headerTransform=indicator_header_transformer,
            removeNull=False,
        )

    related_entities: list = (indicator_data.get("risk", {}) or {}).get("related_entities", [])

    for entity in related_entities:
        entity = entity or {}
        entities_formatted = [
            {
                "entity_id": entity.get("entity_id", ""),
                "entity_type": entity.get("entity_type", ""),
                "entity_name": entity.get("entity_name", ""),
            }
        ]

        human_readable += tableToMarkdown(
            "Domain Related Entities",
            entities_formatted,
            headers=["entity_id", "entity_type", "entity_name"],
            headerTransform=indicator_header_transformer,
            removeNull=False,
        )

    return CommandResults(
        readable_output=human_readable,
        outputs_prefix="Cyberint.ipv4",
        outputs_key_field="value",
        raw_response=indicator_data,
        outputs=indicator_data,
    )


def get_domain_command(
    client: Client,
    args: dict[str, Any],
) -> CommandResults:
    """
    Wrapper for retrieving enrichment for Domain from the feed to the war-room.

    Args:
        client: Cyberint API Client.
        args: Command arguments.

    Returns:
        Outputs indicators.
    """

    value = args.get("value", "")

    indicator = client.retrieve_domain_from_api(value)

    indicator_data = indicator.get("data", {})
    indicator_formatted = [
        {
            "type": (indicator_data.get("entity") or {}).get("type"),
            "value": (indicator_data.get("entity") or {}).get("value"),
            "malicious_score": (indicator_data.get("risk") or {}).get("malicious_score"),
            "ips": (indicator_data.get("enrichment") or {}).get("ips"),
            "occurrences_count": (indicator_data.get("risk") or {}).get("occurrences_count"),
            "registrant_name": (indicator_data.get("enrichment", {}).get("whois") or {}).get("registrant_name"),
            "registrant_email": (indicator_data.get("enrichment", {}).get("whois") or {}).get("registrant_email"),
            "registrant_organization": (indicator_data.get("enrichment", {}).get("whois") or {}).get("registrant_organization"),
            "registrant_country": (indicator_data.get("enrichment", {}).get("whois") or {}).get("registrant_country"),
            "registrant_telephone": (indicator_data.get("enrichment", {}).get("whois") or {}).get("registrant_telephone"),
            "technical_contact_email": (indicator_data.get("enrichment", {}).get("whois") or {}).get("technical_contact_email"),
            "technical_contact_name": (indicator_data.get("enrichment", {}).get("whois") or {}).get("technical_contact_name"),
            "technical_contact_organization": (indicator_data.get("enrichment", {}).get("whois") or {}).get(
                "technical_contact_organization"
            ),
            "registrar_name": (indicator_data.get("enrichment", {}).get("whois") or {}).get("registrar_name"),
            "admin_contact_name": (indicator_data.get("enrichment", {}).get("whois") or {}).get("admin_contact_name"),
            "admin_contact_organization": (indicator_data.get("enrichment", {}).get("whois") or {}).get(
                "admin_contact_organization"
            ),
            "admin_contact_email": (indicator_data.get("enrichment", {}).get("whois") or {}).get("admin_contact_email"),
            "created_date": (indicator_data.get("enrichment", {}).get("whois") or {}).get("created_date"),
            "updated_date": (indicator_data.get("enrichment", {}).get("whois") or {}).get("updated_date"),
            "expiration_date": (indicator_data.get("enrichment", {}).get("whois") or {}).get("expiration_date"),
            "benign": indicator_data.get("benign"),
        }
    ]

    human_readable = tableToMarkdown(
        "Domain Entity",
        indicator_formatted,
        headers=["type", "value", "malicious_score", "occurrences_count", "benign"],
        headerTransform=indicator_header_transformer,
        removeNull=False,
    )

    human_readable += tableToMarkdown(
        "Domain Enrichment",
        indicator_formatted,
        date_fields=["created_date", "updated_date", "expiration_date"],
        headers=[
            "ips",
            "registrant_name",
            "registrant_email",
            "registrant_organization",
            "registrant_country",
            "registrant_telephone",
            "technical_contact_email",
            "technical_contact_name",
            "technical_contact_organization",
            "registrar_name",
            "admin_contact_name",
            "admin_contact_organization",
            "admin_contact_email",
            "created_date",
            "updated_date",
            "expiration_date",
        ],
        headerTransform=indicator_header_transformer,
        removeNull=False,
    )

    detected_activities: list = (indicator_data.get("risk", {}) or {}).get("detected_activities", [])

    for activity in detected_activities or []:
        activities_formatted = [
            {
                "type": (activity or {}).get("type", ""),
                "observation_date": (activity or {}).get("observation_date", ""),
                "description": (activity or {}).get("description", ""),
                "confidence": (activity or {}).get("confidence", ""),
                "occurrences_count": (activity or {}).get("occurrences_count", ""),
            }
        ]

        human_readable += tableToMarkdown(
            "Domain Detected activities",
            activities_formatted,
            date_fields=["observation_date"],
            headers=["type", "observation_date", "description", "confidence", "occurrences_count"],
            headerTransform=indicator_header_transformer,
            removeNull=False,
        )

    related_entities: list = (indicator_data.get("risk", {}) or {}).get("related_entities", [])

    for entity in related_entities or []:
        entities_formatted = [
            {
                "entity_id": (entity or {}).get("entity_id", ""),
                "entity_type": (entity or {}).get("entity_type", ""),
                "entity_name": (entity or {}).get("entity_name", ""),
            }
        ]

        human_readable += tableToMarkdown(
            "Domain Related Entities",
            entities_formatted,
            headers=["entity_id", "entity_type", "entity_name"],
            headerTransform=indicator_header_transformer,
            removeNull=False,
        )

    return CommandResults(
        readable_output=human_readable,
        outputs_prefix="Cyberint.domain",
        outputs_key_field="value",
        raw_response=indicator_data,
        outputs=indicator_data,
    )


def get_file_sha256_command(
    client: Client,
    args: dict[str, Any],
) -> CommandResults:
    """
    Wrapper for retrieving enrichment for file SHA256 hash from the feed to the war-room.

    Args:
        client: Cyberint API Client.
        args: Command arguments.

    Returns:
        Outputs indicators.
    """

    value = args.get("value", "")

    indicator = client.retrieve_file_sha256_from_api(value)

    indicator_data = indicator.get("data", {})
    indicator_formatted = [
        {
            "type": indicator_data.get("entity", {}).get("type", ""),
            "value": indicator_data.get("entity", {}).get("value", ""),
            "malicious_score": indicator_data.get("risk", {}).get("malicious_score", ""),
            "filenames": indicator_data.get("enrichment", {}).get("filenames", []),
            "first_seen": indicator_data.get("enrichment", {}).get("first_seen", ""),
            "download_urls": indicator_data.get("enrichment", {}).get("download_urls", []),
            "benign": indicator_data.get("benign", ""),
        }
    ]

    human_readable = tableToMarkdown(
        "File SHA256 Entity",
        indicator_formatted,
        headers=["type", "value", "malicious_score", "benign"],
        headerTransform=indicator_header_transformer,
        removeNull=False,
    )

    human_readable += tableToMarkdown(
        "File SHA256 Enrichment",
        indicator_formatted,
        date_fields=["first_seen"],
        headers=["filenames", "first_seen", "download_urls"],
        headerTransform=indicator_header_transformer,
        removeNull=False,
    )

    detected_activities: list = (indicator_data.get("risk", {}) or {}).get("detected_activities", [])

    for activity in detected_activities or []:
        activity = activity or {}
        activities_formatted = [
            {
                "type": activity.get("type", ""),
                "observation_date": activity.get("observation_date", ""),
                "description": activity.get("description", ""),
                "confidence": activity.get("confidence", ""),
                "occurrences_count": activity.get("occurrences_count", ""),
            }
        ]

        human_readable += tableToMarkdown(
            "File SHA256 Detected activities",
            activities_formatted,
            date_fields=["observation_date"],
            headers=["type", "observation_date", "description", "confidence", "occurrences_count"],
            headerTransform=indicator_header_transformer,
            removeNull=False,
        )

    related_entities: list = (indicator_data.get("risk", {}) or {}).get("related_entities", [])

    for entity in related_entities or []:
        entity = entity or {}
        entities_formatted = [
            {
                "entity_id": entity.get("entity_id", ""),
                "entity_type": entity.get("entity_type", ""),
                "entity_name": entity.get("entity_name", ""),
            }
        ]

        human_readable += tableToMarkdown(
            "File SHA256 Related Entities",
            entities_formatted,
            headers=["entity_id", "entity_type", "entity_name"],
            headerTransform=indicator_header_transformer,
            removeNull=False,
        )

    return CommandResults(
        readable_output=human_readable,
        outputs_prefix="Cyberint.file_sha256",
        outputs_key_field="value",
        raw_response=indicator_data,
        outputs=indicator_data,
    )


def fetch_indicators_command(
    client: Client,
    params: dict[str, Any],
) -> list[dict[str, Any]]:
    """
    Wrapper for fetching indicators from the feed to the Indicators tab.

    Args:
        client: Cyberint API Client.
        params: Integration parameters.

    Returns:
        Indicators.
    """
    feed_enabled = params.get("feed", True)
    tlp_color = params.get("tlp_color", "")
    feed_tags = argToList(params.get("feedTags"))
    severity_from = arg_to_number(params.get("severity_from")) or 0
    confidence_from = arg_to_number(params.get("confidence_from")) or 0
    feed_names = argToList(params.get("feed_name"))
    indicator_types = argToList(params.get("indicator_type"))
    fetch_interval = arg_to_number(params.get("feedFetchInterval")) or DEFAULT_INTERVAL

    indicators = []

    if feed_enabled:
        # if now-interval is yesterday, call feeds for yesterday too
        if is_x_minutes_ago_yesterday(fetch_interval):
            indicators = fetch_indicators(
                client=client,
                date_time=get_yesterday_time(),
                tlp_color=tlp_color,
                feed_tags=feed_tags,
                feed_names=feed_names,
                indicator_types=indicator_types,
                severity_from=severity_from,
                confidence_from=confidence_from,
            )

        indicators += fetch_indicators(
            client=client,
            tlp_color=tlp_color,
            feed_tags=feed_tags,
            feed_names=feed_names,
            indicator_types=indicator_types,
            severity_from=severity_from,
            confidence_from=confidence_from,
        )
    return indicators


def get_today_time() -> str:
    """Get current date time.

    Returns:
        str: Today date string.
    """
    return datetime.now().strftime(DATE_FORMAT)


def get_yesterday_time() -> str:
    """Get yesterday date time.

    Returns:
        str: Yesterday date string.
    """
    current_time = datetime.now()
    yesterday = current_time - timedelta(days=1)
    return yesterday.strftime(DATE_FORMAT)


def is_x_minutes_ago_yesterday(minutes: int) -> bool:
    """Check if x minutes ago is yesterday.

    Args:
        minutes (int): The amount of minutes to reduce from today.

    Returns:
        bool: True if x minutes ago is yesterday, else False.
    """
    current_time = datetime.now()
    x_minutes_ago = current_time - timedelta(minutes=minutes)
    yesterday = current_time - timedelta(days=1)
    return x_minutes_ago.date() == yesterday.date()


def pick_cvss_collection(cvss: dict[str, Any]) -> tuple[Optional[float], Optional[str], Optional[str]]:
    """
    Picks the most relevant CVSS base score and the matching version + vector string from a Cyberint CVSS object.
    Preference order: v4, then v3, then v2.

    Args:
        cvss (dict): The CVSS object from a Cyberint VulnerabilityDetail response.

    Returns:
        A tuple of (base_score, version, vector_string) all from the same CVSS collection, or (None, None, None).
    """
    for version_key in ("cvss_v4", "cvss_v3", "cvss_v2"):
        collection = cvss.get(version_key) or {}
        if collection.get("base_score") is not None:
            return collection.get("base_score"), collection.get("version"), collection.get("vector_string")
    return None, None, None


def get_cve_command(client: Client, args: dict[str, Any]) -> list[CommandResults]:
    """
    Enriches one or more CVE identifiers with Cyberint vulnerability intelligence.

    Args:
        client: Cyberint API Client.
        args: Command arguments. Expects a 'cve_id' argument (CSV supported).

    Returns:
        One CommandResults entry per CVE.
    """
    cve_ids = argToList(args.get("cve_id"))
    if not cve_ids:
        raise DemistoException("You must supply at least one CVE identifier in the 'cve_id' argument.")

    command_results: list[CommandResults] = []
    for cve_id in cve_ids:
        raw_response = client.retrieve_cve_intelligence(cve_id)
        cve_data = raw_response.get("data", raw_response) if isinstance(raw_response, dict) else {}
        cve_data = cve_data or {}
        # Ensure the CVE identifier is always present in the output, even for a sparse response.
        cve_data.setdefault("cve_id", cve_id)

        cvss = cve_data.get("cvss") or {}
        base_score, cvss_version, cvss_vector = pick_cvss_collection(cvss)
        cwes = cve_data.get("cwes") or []
        cwe_ids = [item.get("cwe_id") if isinstance(item, dict) else item for item in cwes]
        exploited_by = cve_data.get("exploited_by") or []
        actor_names = [item.get("name") if isinstance(item, dict) else item for item in exploited_by]

        indicator = Common.CVE(
            id=cve_data.get("cve_id", cve_id),
            cvss=base_score,
            cvss_score=base_score,
            cvss_version=cvss_version,
            cvss_vector=cvss_vector,
            published=cve_data.get("published"),
            modified=cve_data.get("last_updated"),
            description=cve_data.get("description"),
        )

        human_readable = tableToMarkdown(
            f"Cyberint CVE Intelligence: {cve_id}",
            {
                "CVE ID": cve_data.get("cve_id"),
                "Cyberint Score": cve_data.get("cyberint_score"),
                "CVSS Score": base_score,
                "EPSS": cve_data.get("epss"),
                "CWE": ", ".join(filter(None, cwe_ids)),
                "Risk Factors": ", ".join(cve_data.get("risk_factors") or []),
                "Exploited By": ", ".join(filter(None, actor_names)),
                "Affected Products": ", ".join(cve_data.get("products") or []),
                "Published": cve_data.get("published"),
            },
            removeNull=True,
        )
        command_results.append(
            CommandResults(
                outputs_prefix="Cyberint.CVE",
                outputs_key_field="cve_id",
                outputs=cve_data,
                readable_output=human_readable,
                raw_response=raw_response,
                indicator=indicator,
            )
        )
    return command_results


def summarize_credential_records(records: list[dict[str, Any]]) -> list[dict[str, Any]]:
    """
    Builds a password-free summary of leaked-credential records for human-readable output.

    Args:
        records (list): Leaked-credential records (LeakedCredentialRecord objects).

    Returns:
        A list of summary dictionaries, one per record.
    """
    summary = []
    for record in records:
        entries = record.get("entries") or []
        summary.append(
            {
                "Username": record.get("username"),
                "First Seen": record.get("first_seen"),
                "Last Seen": record.get("last_seen"),
                "Exposures": len(entries),
                "Sources": ", ".join(sorted({entry.get("source") for entry in entries if entry.get("source")})),
            }
        )
    return summary


def filter_credentials_by_last_seen(
    records: list[dict[str, Any]], last_seen_from: Optional[str], last_seen_to: Optional[str]
) -> list[dict[str, Any]]:
    """
    Filters leaked-credential records by their 'last seen' date.

    The leaked-credentials by_domain API does not support date filtering server-side, so it is applied here.

    Args:
        records (list): Leaked-credential records.
        last_seen_from (str): Optional lower bound for the record 'last seen' date.
        last_seen_to (str): Optional upper bound for the record 'last seen' date.

    Returns:
        The filtered list of records.
    """
    if not last_seen_from and not last_seen_to:
        return records
    start = arg_to_datetime(last_seen_from)
    end = arg_to_datetime(last_seen_to)
    filtered = []
    for record in records:
        last_seen = arg_to_datetime(record.get("last_seen"))
        if last_seen is None:
            continue
        if start and last_seen < start:
            continue
        if end and last_seen > end:
            continue
        filtered.append(record)
    return filtered


def get_leaked_credentials_by_domain_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """
    Looks up leaked employee and customer credentials associated with a company domain.

    Args:
        client: Cyberint API Client.
        args: Command arguments. Expects 'domain'; optionally 'last_seen_from', 'last_seen_to' and 'limit'.

    Returns:
        Outputs the leaked credentials.
    """
    domain = args.get("domain")
    if not domain:
        raise DemistoException("You must supply a 'domain' argument.")

    last_seen_from = args.get("last_seen_from")
    last_seen_to = args.get("last_seen_to")
    limit = arg_to_number(args.get("limit"))

    raw_response = client.retrieve_leaked_credentials_by_domain(domain)
    data = raw_response.get("data") if isinstance(raw_response, dict) else None
    data = data or {}
    employee_records = (data.get("employee") or {}).get("raw_data") or []
    customer_records = (data.get("customer") or {}).get("raw_data") or []

    employee_records = filter_credentials_by_last_seen(employee_records, last_seen_from, last_seen_to)
    customer_records = filter_credentials_by_last_seen(customer_records, last_seen_from, last_seen_to)
    if limit:
        employee_records = employee_records[:limit]
        customer_records = customer_records[:limit]

    outputs = {
        "domain": domain,
        "employee_total": len(employee_records),
        "customer_total": len(customer_records),
        "total": len(employee_records) + len(customer_records),
        "employee_credentials": employee_records,
        "customer_credentials": customer_records,
    }

    # Passwords are intentionally excluded from the readable tables to avoid exposing them in the war room.
    human_readable = f"### Cyberint Leaked Credentials for {domain}\n"
    human_readable += f"Employee credentials: {len(employee_records)} | Customer credentials: {len(customer_records)}\n"
    human_readable += tableToMarkdown("Employee Credentials", summarize_credential_records(employee_records), removeNull=True)
    human_readable += tableToMarkdown("Customer Credentials", summarize_credential_records(customer_records), removeNull=True)

    return CommandResults(
        outputs_prefix="Cyberint.LeakedCredential",
        outputs_key_field="domain",
        outputs=outputs,
        readable_output=human_readable,
        raw_response=raw_response,
    )


@logger
def main():
    """
    PARSE AND VALIDATE INTEGRATION PARAMS
    """
    params = demisto.params()
    args = demisto.args()

    base_url = params.get("url")
    access_token = params.get("access_token").get("password")
    insecure = not params.get("insecure", False)
    proxy = params.get("proxy", False)
    feed_enabled = demisto.params().get("feed", True)

    command = demisto.command()
    demisto.debug(f"Command being called is {command}")

    try:
        client = Client(
            base_url=base_url,
            access_token=access_token,
            verify=insecure,
            proxy=proxy,
        )

        if command == "test-module":
            return_results(test_module(client, feed_enabled))

        elif command == "cyberint-get-indicators":
            return_results(get_indicators_command(client, args))

        elif command == "cyberint-get-file-sha256":
            return_results(get_file_sha256_command(client, args))

        elif command == "cyberint-get-domain":
            demisto.debug("cyberint-get-domain")
            return_results(get_domain_command(client, args))

        elif command == "cyberint-get-ipv4":
            demisto.debug("cyberint-get-ipv4")
            return_results(get_ipv4_command(client, args))

        elif command == "cyberint-get-url":
            demisto.debug("cyberint-get-url")
            return_results(get_url_command(client, args))

        elif command == "cyberint-cve-enrich":
            demisto.debug("cyberint-cve-enrich")
            return_results(get_cve_command(client, args))

        elif command == "cyberint-credential-leak-lookup":
            demisto.debug("cyberint-credential-leak-lookup")
            return_results(get_leaked_credentials_by_domain_command(client, args))

        elif command == "fetch-indicators":
            indicators = fetch_indicators_command(client, params)
            demisto.debug(f"Total {len(indicators)} indicators to be submitted")
            for iter_ in batch(indicators, batch_size=5000):
                demisto.debug(f"Submit {len(iter_)} indicators to XSOAR")
                demisto.createIndicators(iter_)
            demisto.debug("Fetch indicators operation completed")

        else:
            raise NotImplementedError(f"Command {command} is not implemented.")

    except Exception as e:
        return_error(f"Failed to execute {command} command.\nError:\n{e!s}")


@logger
def is_execution_time_exceeded(start_time: datetime) -> bool:
    """
    Checks if the execution time so far exceeded the timeout limit.

    Args:
        start_time (datetime): the time when the execution started.

    Returns:
        bool: true, if execution passed timeout settings, false otherwise.
    """
    end_time = datetime.now()
    secs_from_beginning = (end_time - start_time).seconds
    demisto.debug(f"Execution duration is {secs_from_beginning} secs so far")

    return secs_from_beginning > EXECUTION_TIMEOUT_SECONDS


def ioc_header_transformer(header: str) -> str:
    """
    Returns a correct header.
    Args:
        header (Str): header.
    Returns:
        header (Str).
    """
    if header == "detected_activity":
        return "Detected activity"
    if header == "ioc_type":
        return "IoC type"
    if header == "ioc_value":
        return "IoC value"
    if header == "observation_date":
        return "Observation date"
    if header == "severity_score":
        return "Severity score"
    if header == "confidence":
        return "Confidence"
    if header == "description":
        return "Description"
    return string_to_table_header(header)


def indicator_header_transformer(header: str) -> str:
    """
    Returns a correct header.
    Args:
        header (Str): header.
    Returns:
        header (Str).
    """
    if header == "type":
        return "Type"
    if header == "value":
        return "Value"
    if header == "malicious_score":
        return "Malicious score"
    if header == "detected_activities":
        return "Detected activities"
    if header == "related_entities":
        return "Related entities"
    if header == "filenames":
        return "Filenames"
    if header == "first_seen":
        return "First seen"
    if header == "download_urls":
        return "Download URLs"
    if header == "benign":
        return "Benign"
    if header == "observation_date":
        return "Observation date"
    if header == "occurrences_count":
        return "Occurrences count"
    if header == "ips":
        return "IPs"
    if header == "registrant_name":
        return "Whois registrant name"
    if header == "registrant_email":
        return "Whois registrant email"
    if header == "registrant_organization":
        return "Whois registrant organization"
    if header == "registrant_country":
        return "Whois registrant country"
    if header == "registrant_telephone":
        return "Whois registrant telephone"
    if header == "technical_contact_email":
        return "Whois technical contact email"
    if header == "technical_contact_name":
        return "Whois technical contact name"
    if header == "technical_contact_organization":
        return "Whois technical contact organization"
    if header == "registrar_name":
        return "Whois registrar name"
    if header == "admin_contact_name":
        return "Whois admin contact name"
    if header == "admin_contact_organization":
        return "Whois admin contact organization"
    if header == "admin_contact_email":
        return "Whois admin contact email"
    if header == "created_date":
        return "Created date"
    if header == "updated_date":
        return "Updated date"
    if header == "expiration_date":
        return "Expiration date"
    if header == "hostname":
        return "Hostname"
    if header == "domain":
        return "Domain"
    if header == "asn_number":
        return "ASN number"
    if header == "asn_organization":
        return "ASN organization"
    return string_to_table_header(header)


if __name__ in ["__main__", "builtin", "builtins"]:
    main()