CybleThreatIntel
Cyble Threat Intelligence for Vision Users. Must have access to Cyble TAXII Feed to access the threat intelligence.
Data Enrichment & Threat Intelligence · Cyble Threat Intel · Feed
Details
| ID | CybleThreatIntel |
|---|---|
| Provider | Cyble |
| Category | Data Enrichment & Threat Intelligence |
| From Version | 6.2.0 |
| Docker Image | demisto/python3:3.12.13.10116658 |
| Supported Modules | Agentix XSIAM |
README
Cyble Threat Intelligence – Cortex XSOAR Integration
This integration enables Cortex XSOAR to ingest and query Indicators of
Compromise (IOCs) from the Cyble Vision API.
It supports two capabilities:
- IOC Lookup (Interactive command for analysts)
- IOC Fetching (Fetch Indicators)
Overview
The Cyble Vision platform provides enriched, high-fidelity threat
intelligence including malware associations, threat actor links,
behaviour tags, risk scoring, and more.
This integration allows XSOAR to:
- Pull fresh IOCs at scheduled intervals
- Tag, score, and store indicators in the Cortex XSOAR indicator store
- Support analyst lookups for a single IOC via the command line or
playbooks
Configuration
Required Parameters
| Parameter | Description | Example |
|---|---|---|
| Base URL | Cyble Vision API endpoint | https://api.cyble.ai/engine/api/v4 |
| API Key (Access Token) | Cyble Vision API Bearer token | (stored securely in XSOAR) |
| First fetch time (hours) | Number of hours to fetch backward on first run | 2 |
| (1–3 hours allowed) | ||
| Indicator Fetch Limit | Maximum indicators per API page | 100 |
Fetch Behavior
- Fetch is performed in 1-hour chunks until the full range is covered.
- Each page of IOCs is inserted immediately using
demisto.createIndicators. - Fetch uses a retry mechanism (up to 5 attempts per page).
last_runis updated after every chunk.- Supported fetch window: 1–3 hours (anything outside is
automatically corrected).
Commands
📌 1. cyble-vision-ioc-lookup
Lookup a single IOC using the Cyble Vision API.
Command
!cyble-vision-ioc-lookup ioc=<IOC_VALUE>
Arguments
| Name | Required | Description |
|---|---|---|
| ioc | Yes | IOC string (IP / Domain / URL / Hash) |
Outputs
Prefix: CybleIntel.IOCLookup
| Field | Description |
|---|---|
| IOC | IOC value |
| IOC Type | Type (IP / Domain / URL / Hash) |
| First Seen | UTC timestamp |
| Last Seen | UTC timestamp |
| Risk Score | 0–100 |
| Sources | Reporting sources |
| Behaviour Tags | Tags assigned by Cyble |
| Confidence Rating | Low / Medium / High |
| Target Countries | Target geography |
| Target Regions | Regions affected |
| Target Industries | Target verticals |
| Related Malware | Linked malware families |
| Related Threat Actors | Associated threat actors |
Example
!cyble-vision-ioc-lookup ioc=45.67.23.9
📌 2. fetch-indicators
Fetch IOCs from Cyble Vision and insert them into XSOAR’s indicator store.
Execution
This command is not run manually.
It is used by the XSOAR engine when Fetches Indicators is enabled.
Behavior
-
Builds indicators with:
cybleverdictcybleriskscorecyblefirstseencyblelastseencyblebehaviourtagscyblesourcescybletargetcountriescybletargetregionscybletargetindustriescyblerelatedmalwarecyblerelatedthreatactors
- Automatically maps each IOC into XSOAR Indicator fields.
- Updates
last_runafter each successful chunk.
Known Limitations
- Fetching supports hours only (days are not supported).
- Maximum initial backfill is 3 hours.
Support
For issues, contact support@cyble.com
or your assigned Cyble Technical Advisor.
Configuration parameters
tlp_color— Traffic Light Protocol ColorfeedFetchInterval— Feed Fetch Intervalfeed— Fetch indicatorsfeedReliability— Source Reliability (required)feedReputation— Indicator ReputationfeedTags— TagsfeedExpirationPolicy—feedExpirationInterval—feedBypassExclusionList— Bypass exclusion listbase_url— Base URLcredentials— Access Tokeninsecure— Trust any certificate (not secure)proxy— Use system proxy settingscollection— Collection Namediscovery_service— Discovery Serviceinitial_interval— First fetch time (by days) (required)limit— Indicator Fetch Limit
Commands (3)
-
cyble-vision-fetch-taxiiDeprecatedDeprecated command. Do not use.
-
cyble-vision-get-collection-namesDeprecatedDeprecated command. Do not use.
-
cyble-vision-ioc-lookupLookup a single IOC using the Cyble Vision API.
import pytest from unittest.mock import patch, MagicMock from datetime import datetime from CybleThreatIntel import ( Client, calculate_verdict, get_time_range, epoch_to_iso, fmt_date, cyble_ioc_lookup_command, fetch_indicators_command, VerdictEnum, ) # ------------------------------------------------------------------- # HTTP POST – SUCCESS # ------------------------------------------------------------------- @patch("CybleThreatIntel.requests.post") def test_http_post_success(mock_post): resp = MagicMock() resp.status_code = 200 resp.json.return_value = {"ok": True} mock_post.return_value = resp client = Client({"base_url": "https://example.com", "access_token": {"password": "a"}}) r = client.http_post("/y/iocs", {"x": 1}) assert r == {"ok": True} mock_post.assert_called_once() # ------------------------------------------------------------------- # HTTP POST – FAILURE # ------------------------------------------------------------------- @patch("CybleThreatIntel.requests.post") def test_http_post_failure(mock_post): resp = MagicMock() resp.status_code = 500 resp.raise_for_status.side_effect = Exception("Server Error") resp.text = "error" mock_post.return_value = resp client = Client({"base_url": "https://example.com", "access_token": {"password": "a"}}) with pytest.raises(Exception): client.http_post("/y/iocs", {}) # ------------------------------------------------------------------- # DATE RANGE LOGIC # ------------------------------------------------------------------- def test_get_time_range_without_last_run(): now = datetime.utcnow() gte, lte = get_time_range(5, {}) g = datetime.fromisoformat(gte) assert (now - g).seconds <= 5 * 3600 + 5 # small tolerance def test_get_time_range_with_last_run(): now = datetime.utcnow().isoformat() gte, lte = get_time_range(6, {"last_fetch": now}) assert gte == now # ------------------------------------------------------------------- # EPOCH TO ISO # ------------------------------------------------------------------- def test_epoch_to_iso(): ts = 1700000000 result = epoch_to_iso(ts) assert result.endswith("Z") # ------------------------------------------------------------------- # VERDICT MATRIX # ------------------------------------------------------------------- @pytest.mark.parametrize( "risk,conf,expected", [ (10, "Low", VerdictEnum.UNKNOWN.value), (10, "Medium", VerdictEnum.SUSPICIOUS.value), (10, "High", VerdictEnum.NOT_MALICIOUS.value), (30, "Low", VerdictEnum.UNKNOWN.value), (30, "Medium", VerdictEnum.SUSPICIOUS.value), (70, "High", VerdictEnum.MALICIOUS.value), (80, "Low", VerdictEnum.SUSPICIOUS.value), (80, "High", VerdictEnum.MALICIOUS.value), ], ) def test_calculate_verdict_values(risk, conf, expected): assert calculate_verdict(risk, conf) == expected # ------------------------------------------------------------------- # IOC LOOKUP COMMAND – NO RESULTS # ------------------------------------------------------------------- @patch("CybleThreatIntel.return_error") @patch("CybleThreatIntel.Client.ioc_lookup") def test_ioc_lookup_no_results(mock_lookup, mock_return): mock_lookup.return_value = {"data": {"iocs": []}} c = Client({"base_url": "x", "access_token": {"password": "a"}}) result = cyble_ioc_lookup_command(c, {"ioc": "1.1.1.1"}) assert "No results found" in result.readable_output # ------------------------------------------------------------------- # IOC LOOKUP – WITH RESULTS # ------------------------------------------------------------------- @patch("CybleThreatIntel.Client.ioc_lookup") def test_ioc_lookup_success(mock_lookup): mock_lookup.return_value = {"data": {"iocs": [{"ioc": "SAMPLE_IOC", "ioc_type": "custom", "first_seen": 1700000000}]}} c = Client({"base_url": "x", "access_token": {"password": "a"}}) result = cyble_ioc_lookup_command(c, {"ioc": "SAMPLE_IOC"}) assert result.outputs["IOC"] == "SAMPLE_IOC" # ------------------------------------------------------------------- # FETCH INDICATORS – MAIN LOGIC # ------------------------------------------------------------------- # ------------------------------------------------------------------- # FETCH INDICATORS – RETRY FAILURE # ------------------------------------------------------------------- @patch("CybleThreatIntel.demisto") def test_fetch_indicators_retry_fail(mock_demisto): mock_demisto.args.return_value = {} mock_demisto.getLastRun.return_value = {} client = Client({"base_url": "x", "access_token": {"password": "a"}}) client.fetch_iocs = MagicMock(side_effect=Exception("fail")) params = {"first_fetch": 1, "max_fetch": 50} count = fetch_indicators_command(client, params) assert count == 0 from unittest.mock import Mock def test_calculate_verdict_invalid_inputs(): assert calculate_verdict("bad", "weird") == "Unknown" assert calculate_verdict(-10, "low") == "Unknown" assert calculate_verdict(200, "high") == "Malicious" def test_ioc_lookup_missing_argument(mocker): client = Mock() mocker.patch("CybleThreatIntel.return_error", side_effect=Exception("Missing required argument: ioc")) with pytest.raises(Exception) as e: cyble_ioc_lookup_command(client, {}) assert "Missing required argument: ioc" in str(e.value) def test_client_init_empty(mocker): params = {} client = Client(params) assert client.base_url == "" assert client.access_token == "" assert client.headers["Authorization"] == "Bearer " def test_client_http_post_failure(mocker): client = Client({"base_url": "http://test.com", "access_token": {"password": "token"}}) mock_resp = mocker.Mock() mock_resp.status_code = 400 mock_resp.text = "Bad request" mock_resp.raise_for_status.side_effect = Exception("HTTP error") mocker.patch("requests.post", return_value=mock_resp) try: client.http_post("/endpoint", {"key": "value"}) except Exception as e: assert "HTTP error" in str(e) def test_epoch_to_iso_invalid(): assert epoch_to_iso("invalid") is None def test_fmt_date_none_and_invalid(): assert fmt_date(None) == "None" assert "invalid" in fmt_date("invalid") def test_client_init_edge_case(mocker): # edge case: empty access_token dict and trailing slash in URL client = Client({"base_url": "https://api.test.com/", "access_token": {"password": ""}}) assert client.base_url == "https://api.test.com" assert client.access_token == "" def test_epoch_to_iso_invalid_timestamp(): # invalid timestamp should return None assert epoch_to_iso("not_a_timestamp") is None def test_calculate_verdict_edge_cases(): # risk_score None, confidence_rating None assert calculate_verdict(None, None) == "Unknown" # extreme low/high values beyond 0-100 assert calculate_verdict(-50, "Low") == "Unknown" assert calculate_verdict(150, "High") == "Malicious" def test_get_time_range_first_run_with_last_fetch(): last_run = {"last_fetch": "2025-12-01T00:00:00"} gte, lte = get_time_range(5, last_run) assert gte == "2025-12-01T00:00:00"