CTIX v3

Integrates with Cyware Intel Exchange to enrich indicators, fetch incidents and threat intelligence indicators, manage tags and notes, view related objects, perform vulnerability lookups, and run generic API requests.

Data Enrichment & Threat Intelligence · Cyware Intel Exchange · Feed

Details

IDCTIX v3
ProviderCyware Labs
CategoryData Enrichment & Threat Intelligence
From Version6.0.0
Docker Imagedemisto/python3:3.12.13.10404775
Supported ModulesAgentix XSIAM

README

The Cyware Intel Exchange integration allows users to fetch threat intelligence incidents and indicators, enrich IP, domain, URL, and file data, manage tags and notes, perform vulnerability lookups, and run generic API requests. The integration was tested with Cyware Intel Exchange version 3.x.

Configure Cyware Intel Exchange in Cortex XSOAR

Parameter Description Required
Endpoint URL Enter the endpoint URL of your Cyware Intel Exchange instance, e.g. https://example.cyware.com/ctixapi/. True
Access Key Enter the Access Key from the Cyware Intel Exchange application. True
Secret Key Enter the Secret Key from the Cyware Intel Exchange application. True
Trust any certificate (not secure) Specify whether to trust any certificate (not secure). False
Use system proxy settings Specify whether to use system proxy settings. False
Timeout The connection timeout, in seconds. Default is 180 seconds. False
Fetch incidents Enable to fetch CTIX reports as Cortex XSOAR incidents. False
Incident type The incident type to assign to fetched CTIX reports. Default is “CTIX Intel”. False
Classifier The classifier to apply to fetched CTIX reports. Default is “CTIX v3 Classifier”. False
Mapper (incoming) The incoming mapper to apply to fetched CTIX reports. Default is “CTIX v3 Incoming Mapper”. False
Source Reliability Reliability of the source providing the intelligence data. Applies to fetched incidents and to the reliability of results returned by the domain, URL, IP, file, and CVE reputation commands — not to indicators fetched via the indicator feed (see the separate Source Reliability parameter below). Default is “C - Fairly reliable”. False
First fetch time How far back to fetch on the first run (e.g., 3 days, 7 days, 1 month). Used as the initial lookback window for both incident fetch and indicator feed fetch when no previous run exists. Default is “3 days”. False
Maximum number of incidents per fetch The maximum number of incidents to return per fetch run. Allowed range is 1-200. Default is 10. False
Incident Fetch CQL Query The custom CQL query used when fetching incidents. Updates the default CQL query. For example, type = “report” AND confidence_score = “90”. False
Fetch indicators Enable to fetch CTIX indicators from Saved Result Sets. False
Saved Result Set Label The label name of the Saved Result Set to pull indicators from. False
Saved Result Set Version The version of Saved Result Set to use. Possible values are: v2, v3. False
Retrieve Enriched Data Whether to enrich indicators via bulk IOC lookup (with relations and enrichment data) before ingestion. False
Feed Fetch Interval The frequency at which the platform polls CTIX for new indicators (e.g. “30 minutes”, “1 hour”, “12 hours”). Controls the Cortex XSOAR feed scheduler cadence. Default is “12 hours”. False
Source Reliability Reliability of the source providing the intelligence data. Applies only to indicators retrieved via the indicator feed — not to fetched incidents or reputation commands (see the separate Source Reliability parameter above). Default is “A - Completely reliable”. True
Indicator Reputation Indicators from this integration instance will be marked with this reputation. False
Traffic Light Protocol Color The Traffic Light Protocol (TLP) designation to apply to indicators fetched from the feed. Possible values are: RED, AMBER, GREEN, WHITE. 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
Tags Tags to apply to indicators fetched via the indicator feed. Supports CSV values. False

Commands

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

ctix-create-tag


Create a new tag in the Cyware Intel Exchange platform

Base Command

ctix-create-tag

Input

Argument Name Description Required
tag_name Enter the tag name. Required
color New tag’s name for the defined colour. If no colour selected, colour grey will be given. Possible values are: blue, purple, orange, red, green, yellow, turquoise, pink, light-red, grey. Required

Context Output

Path Type Description
CTIX.Tag.name string Name of the tag
CTIX.Tag.tag_type string Type of the tag
CTIX.Tag.colour_code string Colour code of the tag
CTIX.Tag.id string Id of the created Tag
CTIX.Tag.created number Timestamp when the tag was created.
CTIX.Tag.modified number Timestamp when the tag was modified.

Command Example

!ctix-create-tag tag_name=xsoar_test_trial color=blue

Context Example

{
    "colour_code": null,
    "created": 1652077948,
    "created_by": "40ab0f84-fb39-4444-95b2-cd155f574aa2",
    "id": "47662c77-b419-419c-9bcf-420e05b01067",
    "modified": 1652077948,
    "modified_by": "40ab0f84-fb39-4444-95b2-cd155f574aa2",
    "name": "xsoar_test_temp",
    "type": "manual"
}

ctix-get-tags


Get a paginated list of tags.

Base Command

ctix-get-tags

Input

Argument Name Description Required
page Page number for pagination. Default value is 1. Optional
page_size Page size for pagination. Default value is 10. Optional
q Search query used to filter results Optional

Context Output

Path Type Description
CTIX.Tag.name string Name of the tag
CTIX.Tag.id string ID of the tag
CTIX.Tag.colour_code string Hex colour code associated with tag
CTIX.Tag.tag_type string Type of the tag
CTIX.Tag.created number Timestamp when the tag was created
CTIX.Tag.modified number Timestamp when the tag was modified

Command Example


#### Context Example

```json
{"next": "tags/?page=2&page_size=1&AccessID=sasfafs-asasvsfasf-vasvasf&Expires=1652078371&Signature=jndjaksbdakbsjdkabscbkjb",
 "page_size": 1,
 "previous": null,
 "results": [{"colour_code": null,
              "created": 1652077948,
              "created_by": {"email": "dummy.account@test.com",
                             "first_name": "dummy",
                             "id": "40ab0f84-fb39-4444-95b2-cd155f574aa2",
                             "last_name": "account"},
              "id": "47662c77-b419-419c-9bcf-420e05b01067",
              "modified": 1652077948,
              "modified_by": {"email": "dummy.account@test.com",
                              "first_name": "dummy",
                              "id": "40ab0f84-fb39-4444-95b2-cd155f574aa2",
                              "last_name": "account"},
              "name": "xsoar_test_temp",
              "type": "manual"}],
 "total": 10}

ctix-disable-or-enable-tags


Enable or disable tags in the Cyware Intel Exchange platform.

Base Command

ctix-disable-or-enable-tags

Input

Argument Name Description Required
tag_ids IDs of the tags to enable or disable. Supports multiple IDs as a comma-separated list. Required
action Action to be performed on the tag. Possible values are: enabled, disabled. The default value is disabled. Optional

Context Output

Path Type Description
CTIX.TagAction.result string Status of the tag action

Command Example

!ctix-disable-or-enable-tags tag_ids=47662c77-b419-419c-9bcf-420e05b01067 action=disabled

Context Example

{"result": "Action Successfully Executed"}

ctix-allowed-iocs


Add a list of same type IOCs to the allowed list.

Base Command

ctix-allowed-iocs

Input

Argument Name Description Required
type Type of IOC. Possible values are: ipv4-addr, ipv6-addr, autonomous-system, email-addr, MD5, SHA-1, SHA-224, SHA-256, SHA-384, SHA-512, SSDEEP, url, cidr, domain-name, mutex, windows-registry-key, user-agent. Required
values Values for the specified IOC type. Required
reason Reason for adding the IOCs to the allowed list. Required

Context Output

Path Type Description
CTIX.Details.invalid unknown List of invalid IOCs provided in the request.
CTIX.Details.new_created unknown List of IOCs added to the whitelist.
CTIX.Details.already_exists unknown List of IOCs that already exist.

Command Example

!ctix-allowed-iocs reason=test type="ipv4-addr" values=x.x.x.x,x.x.x.x

Context Example

{
  "details":{
   "already_exists": [
    "x.x.x.x",
    "x.x.x.x"
   ],
   "invalid": [],
   "new_created": []
  }
}

ctix-get-allowed-iocs


Get a paginated list of allowed IOCs.

Base Command

ctix-get-allowed-iocs

Input

Argument Name Description Required
page Page number for pagination. Default value is 1. Optional
page_size Page size for pagination. Default value is 10. Optional
q Search query used to filter results Optional

Context Output

Path Type Description
CTIX.IOC.id string ID of the object.
CTIX.IOC.include_emails boolean Indicates whether the associated email addresses are whitelisted.
CTIX.IOC.include_sub_domains boolean Indicates whether the associated subdomains are whitelisted.
CTIX.IOC.include_urls boolean Indicates whether the associated URLs are whitelisted.
CTIX.IOC.type string Type of the IOC.
CTIX.IOC.value string Value of the IOC.
CTIX.IOC.created number Timestamp when the IOC was created.
CTIX.IOC.modified number Timestamp when the IOC was last modified.

Command Example

!ctix-get-allowed-iocs q=type=indicator

Context Example

{"next": "allowed/?page=2&page_size=1", "page_size": 1, "previous": null,
 "results": [{"created": 1652084983, "created_by": {"email":
 "dumy.account@example.com", "first_name": "dumy", "id":
 "40ab0f84-fb39-4444-95b2-cd155f574aa2", "last_name": "account"}, "follow":
 true, "id": "2df4a0ad-b1dd-4a4c-bf71-dcdefce0dcf9", "include_emails": false,
 "include_subdomains": false, "include_urls": false, "modified": 1652097309,
 "modified_by": {"email": "dummt.acount@example.com", "first_name": "", "id":
 "4a5f744c-800a-4fcd-be06-53f4b1b8f966", "last_name": ""}, "type":
 "ipv4-addr", "value": "x.x.x.x"}], "total": 5}

ctix-remove-allowed-ioc


Removes an allowed IOC using the specified ID.

Base Command

ctix-remove-allowed-ioc

Input

Argument Name Description Required
ids IDs of the allowed IOCs. Required

Context Output

Path Type Description
details string Describes the status of the action.

Command Example

!ctix-remove-allowed-ioc ids=7a33a7ac-ab54-412f-a725-f35c208a54ea

Context Example

{
 "details": "Action applied succesfully"
}

ctix-get-threat-data


Command for querying and listing threat data.

Base Command

ctix-get-threat-data

Input

Argument Name Description Required
query Search query used to filter results Required
page Page number for pagination. Default value is 1. Optional
page_size Page size for pagination. Default value is 10. Optional

Context Output

Path Type Description
CTIX.ThreatData.confidence_score number Confidence Score of the IOC
CTIX.ThreatData.confidence_type string Confidence Type of the IOC
CTIX.ThreatData.created number Timestamp indicating when the IOC was created in the source.
CTIX.ThreatData.ctix_created number Timestamp indicating when the IOC was created in Cyware Intel Exchange.
CTIX.ThreatData.ctix_modified number Timestamp indicating when the IOC was modified in Cyware Intel Exchange.
CTIX.ThreatData.id string ID of the IOC in Cyware Intel Exchange.
CTIX.ThreatData.indicator_type string Type of the Indicator
CTIX.ThreatData.ioc_type string Type of IOC
CTIX.ThreatData.is_actioned boolean Indicates whether the IOC has been actioned
CTIX.ThreatData.is_deprecated boolean Indicates whether the IOC is deprecated
CTIX.ThreatData.is_false_positive boolean Indicates whether the IOC is a false positive
CTIX.ThreatData.is_reviewed boolean Indicates whether the IOC has been reviewed
CTIX.ThreatData.is_revoked boolean Indicates whether the IOC has been revoked
CTIX.ThreatData.is_watchlist boolean Indicates whether the IOC is on the watchlist
CTIX.ThreatData.is_whitelisted boolean Indicates whether the IOC is whitelisted
CTIX.ThreatData.modified boolean Timestamp indicating when the IOC was last modified
CTIX.ThreatData.name boolean Name of the indicator
CTIX.ThreatData.risk_severity boolean Risk severity of the indicator
CTIX.ThreatData.source_collections unknown Source Collections of the Indicator
CTIX.ThreatData.source_confidence string Source Confidence of the indicator
CTIX.ThreatData.sources unknown List of Sources for the indicator
CTIX.ThreatData.sub_type string Sub Type of the IOC
CTIX.ThreatData.tlp string TLP of the indicator
CTIX.ThreatData.type string Type of the IOC
CTIX.ThreatData.valid_from number Date from which IOC is valid

Command Example

!ctix-get-threat-data query=type=indicator

Context Example

{
  "next": null,
  "page_size": 10,
  "previous": null,
  "results": [
   {"analyst_score": null,
     "analyst_tlp": null,
     "confidence_score": 50,
     "confidence_type": "ctix",
     "country": null,
     "created": 1652081902,
     "ctix_created": 1652081903,
     "ctix_modified": 1652081903,
     "first_seen": null,
     "id": "1ff2a18a-0574-4015-bbec-bc7692dccb14",
     "indicator_type": "domain-name",
     "ioc_type": "domain-name",
     "is_actioned": false,
     "is_deprecated": false,
     "is_false_positive": false,
     "is_reviewed": false,
     "is_revoked": false,
     "is_watchlist": false,
     "is_whitelisted": false,
     "last_seen": null,
     "modified": 1652081902,
     "name": "example.com",
     "null": [],
     "primary_attribute": null,
     "published_collections": [],
     "risk_severity": "UNKNOWN",
     "source_collections": [{"id": "1981f5f6-49d4-4cad-97b7-8b2d276d2956",
           "name": "dummy"}],
     "source_confidence": "HIGH",
     "sources": [{"id": "48e5966e-5d1b-4cf9-8e79-306aa8702a28",
         "name": "dummy",
         "source_type": "RSS_FEED"}],
     "sub_type": "value",
     "subscriber_collections": [],
     "subscribers": [],
     "tags": [],
     "tlp": "AMBER",
     "type": "indicator",
     "valid_from": 1652081902,
     "valid_until": null}],
 "total": 1}

ctix-get-saved-searches


List saved searches with pagination.

Base Command

ctix-get-saved-searches

Input

Argument Name Description Required
page Page number for pagination. Default value is 1. Optional
page_size Number of results per page. Default value is 10. Optional

Context Output

Path Type Description
CTIX.SavedSearch.id string ID of the object
CTIX.SavedSearch.editable boolean Indicates whether the saved search is editable
CTIX.SavedSearch.is_threat_data_search boolean Indicates whether the saved search is a threat data search
CTIX.SavedSearch.name string Name of the saved search
CTIX.SavedSearch.order number Order of the saved search
CTIX.SavedSearch.pinned boolean Indicates whether the saved search is pinned
CTIX.SavedSearch.query string Query of the saved search
CTIX.SavedSearch.shared_type string Shared type of the saved search
CTIX.SavedSearch.type string Type of the saved search
CTIX.SavedSearch.meta_data unknown Metadata of the saved search

Command Example


#### Context Example

```json
{
 "next": null,
 "page_size": 10,
 "previous": null,
 "results": [
   {
  "created_by": {
    "email": "system.default@example.com",
    "first_name": "System",
    "id": "e99b5f93-4ae8-4560-a848-a4fbae3f4f26",
    "last_name": "Default"
  },
  "description": null,
  "editable": false,
  "id": "d5b54bc7-3b3f-424b-b08d-5e8cf746e998",
  "is_threat_data_search": true,
  "meta_data": null,
  "name": "Indicator",
  "order": 0,
  "pinned": false,
  "query": "type =indicator",
  "shared_type": "global",
  "shared_users": [

  ],
  "type": "cql"
   }
 ],
 "total": 1
}

ctix-get-server-collections


List source collections with pagination.

Base Command

ctix-get-server-collections

Input

Argument Name Description Required
page Page number for pagination. Default value is 1. Optional
page_size Number of results per page. Default value is 15. Optional

Context Output

Path Type Description
CTIX.ServerCollection.name string Name of the server
CTIX.ServerCollection.id string ID of the object
CTIX.ServerCollection.inbox boolean Indicates whether the inbox is enabled.
CTIX.ServerCollection.is_active boolean Indicates whether the object is active.
CTIX.ServerCollection.is_editable boolean Indicates whether the object is editable.
CTIX.ServerCollection.polling boolean Indicates whether the object polling is enabled.
CTIX.ServerCollection.type string Returns the Object Type
CTIX.ServerCollection.description string Description of the object
CTIX.ServerCollection.created number Timestamp indicating when the object was created

Command Example


#### Context Example

```json
{"next": "collection/?page=2&page_size=1", "previous": null, "page_size": 1,
 "total": 7, "results": [{"id": "83b5fd74-8ca0-4f28-a173-1d6863b2acb4",
 "name": "collection", "description": "with description", "is_active": true,
 "type": "DATA_FEED", "is_editable": true, "polling": false, "inbox": true,
 "created": 1652080268, "has_subscribed": null}], "subscriber_name": ""}

ctix-get-actions


List enrichment tools.

Base Command

ctix-get-actions

Input

Argument Name Description Required
page Page number for pagination. Default is 1. Optional
page_size Number of results per page. Default is 15. Optional
object_type Type of the object. Optional
action_type Type of the action. Optional

Context Output

Path Type Description
CTIX.Action.action_name string Name of the Action
CTIX.Action.action_type unknown Description of the action
CTIX.Action.actioned_on number Timestamp of when the action was taken
CTIX.Action.app_name string Name of the app for the action
CTIX.app_type string Type of the app
CTIX.Action.id string ID of the action
CTIX.Action.object_type string Type of the action

Command Example

!ctix-get-actions action_type=manual object_type=indicator

Context Example

{
 "next": "actions/?page=2&page_size=1&actions_type=manual&object_type=indicator",
 "page_size": 1,
 "previous": null,
 "results": [
   {
  "action_name": "Update Analyst Score",
  "action_type": "manual",
  "actioned_by": {
    "email": "dummy.email@test.com",
    "first_name": "test",
    "id":"40ab0f84-fb39-4444-95b2-cd155f574aa2",
    "last_name": "account"
  },
  "actioned_on": 1651646873,
  "app_name": "CTIX",
  "app_response": {

  },
  "app_type": "ctix",
  "id": "e8fe8d27-6329-4c0b-a3c0-be104be4de55",
  "object_id": "19176d96-716d-48aa-af15-dfeff22e72e2",
  "object_type": "indicator",
  "rule_id": null,
  "rule_name": null,
  "source_id": null,
  "tool": null
   }
 ],
 "total": 38459
  }

ctix-add-indicator-as-false-positive


Marks indicators as false positive in bulk.

Base Command

ctix-add-indicator-as-false-positive

Input

Argument Name Description Required
object_ids Comma-separated list of indicator IDs. Required
object_type Type of object. Possible values are: attack-pattern, campaign, course-of-action, custom-object, grouping, identity, indicator, infrastructure, intrusion-set, location, malware, malware-analysis, observed-data, opinion, report, threat-actor, tool, note, vulnerability, artifact, directory, email-addr, user-account, email-message, file, ipv4-addr, ipv6-addr, mac-addr, autonomous-system, network-traffic, domain-name, process, software, windows-registry-key, mutex, url, observable, x509-certificate. Required

Context Output

Path Type Description
CTIX.IndicatorFalsePositive.message unknown Returns the result of the false positive action

Command Example

!ctix-add-indicator-as-false-positive object_ids=19176d96-716d-48aa-af15-dfeff22e72e2,531e47a6-d7cd-47be-ae21-a3260518d4a5 object_type=indicator

Context Example

{"message":"Action Successfully Executed"}

ctix-ioc-manual-review


Add IOCs to manual review in bulk.

Base Command

ctix-ioc-manual-review

Input

Argument Name Description Required
object_ids IDs of the objects to add for manual review. Required
object_type Type of the object. Possible values are: attack-pattern, campaign, course-of-action, custom-object, grouping, identity, indicator, infrastructure, intrusion-set, location, malware, malware-analysis, observed-data, opinion, report, threat-actor, tool, note, vulnerability, artifact, directory, email-addr, user-account, email-message, file, ipv4-addr, ipv6-addr, mac-addr, autonomous-system, network-traffic, domain-name, process, software, windows-registry-key, mutex, url, observable, x509-certificate. Required

Context Output

Path Type Description
CTIX.IOCManualReview.message unknown Result of the IOC manual review

Command Example

!ctix-ioc-manual-review object_ids=f3064a83-304e-4801-bec2-2f26a432bfd2,0aced40d-9a83-46cd-a92b-0c776c92594c object_type=indicator

Context Example

{
    "message": "Action Successfully Executed"
}

ctix-deprecate-ioc


Deprecates IOCs in bulk

Base Command

ctix-deprecate-ioc

Input

Argument Name Description Required
object_ids Comma-separated list of object IDs. Required
object_type Type of the object. Required

Context Output

Path Type Description
CTIX.DeprecateIOC unknown Result of the IOC deprecation request

Command Example

!ctix-deprecate-ioc object_ids=f3064a83-304e-4801-bec2-2f26a432bfd2,0aced40d-9a83-46cd-a92b-0c776c92594c object_type=indicator

Context Example

{
    "message": "Action Successfully Executed"
}

ctix-add-analyst-tlp


Add analyst TLP

Base Command

ctix-add-analyst-tlp

Input

Argument Name Description Required
object_id ID of the object to add analyst TLP. Required
object_type Type of the object. Required
data Object details you want to add for the analyst TLP. Required

Context Output

Path Type Description
CTIX.AddAnalystTLP unknown Result of adding the analyst TLP.

Command Example

!ctix-add-analyst-tlp object_id=19176d96-716d-48aa-af15-dfeff22e72e2 object_type=indicator data={\"analyst_tlp\":\"GREEN\"}

Context Example

{
    "message": "Action Successfully Executed"
}

ctix-add-analyst-score


Add analyst score for threat data.

Base Command

ctix-add-analyst-score

Input

Argument Name Description Required
object_id ID of the object to add analyst score. Required
object_type Type of the object. Required
data Object details you want to add for the analyst score. Required

Context Output

Path Type Description
CTIX.AddAnalystScore unknown Result of adding analyst score to threat data

Command Example

!ctix-add-analyst-score data={"analyst_score":10} object_id=19176d96-716d-48aa-af15-dfeff22e72e2 object_type=indicator

Context Example

{
    "message": "Action Successfully Executed"
}

ctix-saved-result-set


Retrieves Threat Data from the Saved Result Set

Base Command

ctix-saved-result-set

Input

Argument Name Description Required
page Page number for pagination. Default is 1. Optional
page_size Page size for pagination. Default is 10. Optional
label_name Tag name for filtering results. Optional
version Version of the Saved Result Set to Use. Allowed Values are v2 and v3 Optional

Context Output

Path Type Description
CTIX.SavedResultSet.analyst_score number Analyst score of the IOC
CTIX.SavedResultSet.analyst_tlp string Analyst TLP of the IOC
CTIX.SavedResultSet.confidence_score number Confidence score of the IOC
CTIX.SavedResultSet.confidence_type string Confidence type of the IOC
CTIX.SavedResultSet.country string Country of origin for the IOC
CTIX.SavedResultSet.created number Timestamp of when the IOC was created
CTIX.SavedResultSet.ctix_created number IOC date of creation in CTIX
CTIX.SavedResultSet.ctix_modified number IOC date of modification in CTIX
CTIX.SavedResultSet.first_seen date IOC timestamp when it was first seen
CTIX.SavedResultSet.id number Object ID of the IOC
CTIX.SavedResultSet.indicator_type string Type of the indicator
CTIX.SavedResultSet.ioc_type string Type of the IOC
CTIX.SavedResultSet.is_actioned boolean If there is any action taken on the indicator
CTIX.SavedResultSet.is_deprecated boolean If the indicator is deprecated or not
CTIX.SavedResultSet.is_false_positive boolean Value of the indicator is false positive or not
CTIX.SavedResultSet.is_reviewed boolean Whether the indicator reviewed or not
CTIX.SavedResultSet.is_revoked boolean Whether the indicator is revoked or not
CTIX.SavedResultSet.is_watchlist boolean Whether the indicator is under watchlist or not
CTIX.SavedResultSet.is_whitelisted boolean Whether the indicator is whitelisted or not
CTIX.SavedResultSet.last_seen date Timestamp of the when the IOC was last seen
CTIX.SavedResultSet.modified date Timestamp of the when the IOC was modified
CTIX.SavedResultSet.name string Name of the indicator
CTIX.SavedResultSet.null unknown null
CTIX.SavedResultSet.primary_attribute string Primary attribute of the IOC
CTIX.SavedResultSet.published_collections unknown Published collections of the IOC
CTIX.SavedResultSet.risk_severity unknown Risk severity of the IOC
CTIX.SavedResultSet.source_collections unknown Source collections of the IOC
CTIX.SavedResultSet.name string Name of the IOC
CTIX.SavedResultSet.sources unknown Sources of the IOC
CTIX.SavedResultSet.sub_type unknown Sub type of the IOC
CTIX.SavedResultSet.subscriber_collections unknown Subscription collections of the IOC
CTIX.SavedResultSet.subscribers unknown Subscribers of the IOC
CTIX.SavedResultSet.tags unknown Tags on the IOC
CTIX.SavedResultSet.tlp unknown TLP of the IOC
CTIX.SavedResultSet.type unknown Type of the IOC
CTIX.SavedResultSet.valid_from unknown Timestamp from when the IOC is valid
CTIX.SavedResultSet.valid_until unknown Timestamp till then the IOC is valid

Command Example

!ctix-saved-result-set label_name=malware-families

Context Example

{"next": "threat-data/list/?page=2&page_size=1", "page_size": 1, "previous":
 null, "results": [{"analyst_score": null, "analyst_tlp": null,
 "confidence_score": null, "confidence_type": "ctix", "country": null,
 "created": 1652111918, "ctix_created": 1652111957, "ctix_modified":
 1652111957, "first_seen": null, "id":
 "670afacb-2f72-42fe-84cc-b2022ba6a7ed", "indicator_type": null, "ioc_type":
 null, "is_actioned": false, "is_deprecated": false, "is_false_positive":
 false, "is_reviewed": false, "is_revoked": false, "is_watchlist": false,
 "is_whitelisted": false, "last_seen": null, "modified": 1652111949, "name":
 "Test12344", "null": [], "primary_attribute": null, "published_collections":
 [], "risk_severity": null, "source_collections": [{"id":
 "32b98724-8625-4af2-ad83-43b4b5c50885", "name": "Test12344"}],
 "source_confidence": "NONE", "sources": [{"id":
 "5968d895-424f-4271-a1d3-2b01041a17bb", "name": "Test12344", "source_type":
 "WEB_SCRAPPER"}], "sub_type": null, "subscriber_collections": [],
 "subscribers": [], "tags": [], "tlp": "AMBER", "type": "report",
 "valid_from": null, "valid_until": null}], "total": 353243}

ctix-add-tag-indicator


Add tag to indicator

Base Command

ctix-add-tag-indicator

Input

Argument Name Description Required
page Page number for pagination. Default is 1. Optional
page_size Total number of results to be fetched. Default is 10. Optional
q Search query for filtering results. Optional
object_id Object ID of the indicator. Optional
object_type Object type of the indicator. Optional
tag_id Tag ID to add to the indicator. Optional

Context Output

Path Type Description
CTIX.TagUpdation.meesage unknown Result of the add indicator tag request

Command Example

!ctix-add-tag-indicator object_id=19176d96-716d-48aa-af15-dfeff22e72e2 object_type=indicator tag_id=fb35000b-82e7-4440-8f18-8b63bba5b372

Context Example

{
    "message": "Action Successfully Executed"
}

ctix-remove-tag-from-indicator


Remove tag from indicator

Base Command

ctix-remove-tag-from-indicator

Input

Argument Name Description Required
page Page number for pagination. Default is 1. Optional
page_size Total number of results to be fetched. Default is 10. Optional
q Search query for filtering results. Optional
object_id Object ID of the indicator. Optional
object_type Object type of the indicator. Optional
tag_id Tag ID to remove from the indicator. Optional

Context Output

Path Type Description
CTIX.TagUpdation.message unknown Result of the remove indicator tag request

Command Example

!ctix-remove-tag-from-indicator object_id=19176d96-716d-48aa-af15-dfeff22e72e2 object_type=indicator tag_id=fb35000b-82e7-4440-8f18-8b63bba5b372

Context Example

{
    "message": "Action Successfully Executed"
}

ctix-search-for-tag


Search for a tag

Base Command

ctix-search-for-tag

Input

Argument Name Description Required
page Page number for pagination. Default is 1. Optional
page_size Total number of results to be fetched. Default is 10. Optional
q Search query for filtering results. Optional

Context Output

Path Type Description
CTIX.SearchTag.colour_code unknown Colour code of the tag
CTIX.SearchTag.created number Timestamp of when the tag was created
CTIX.SearchTag.created_by unknown Details of the person who created the tag
CTIX.SearchTag.id string ID of the tag
CTIX.SearchTag.modified number Timestamp of when the tag was modified
CTIX.SearchTag.modified_by unknown Details of the person who modified the tag
CTIX.SearchTag.name unknown Name of the tag
CTIX.SearchTag.type unknown Type of the tag

Command Example

!ctix-search-for-tag q=xsoar_test_trial

Context Example

{"next": "tags/?page=2&page_size=1", "page_size": 1, "previous": null,
 "results": [{"colour_code": null, "created": 1652113918, "created_by":
 {"email": "dummy.account@example.com", "first_name": "dummy", "id":
 "40ab0f84-fb39-4444-95b2-cd155f574aa2", "last_name": "account"}, "id":
 "68981db8-6deb-41f0-9727-74ad81cf47b2", "modified": 1652113918,
 "modified_by": {"email": "dummy.account@example.com", "first_name":
 "dummy", "id": "40ab0f84-fb39-4444-95b2-cd155f574aa2", "last_name":
 "account"}, "name": "xsoar_test", "type": "manual"}], "total": 39893}

ctix-get-indicator-details


Get indicator details

Base Command

ctix-get-indicator-details

Input

Argument Name Description Required
page Page number for pagination. Default is 1. Optional
page_size Total number of results to be fetched. Default is 10. Optional
object_id Object ID of the indicator. Optional
object_type Object type of the indicator. Optional

Context Output

Path Type Description
CTIX.IndicatorDetails.aliases string Aliases of the tag if any
CTIX.IndicatorDetails.analyst_description string Analyst description provided if any
CTIX.IndicatorDetails.analyst_score number Analyst score of the indicator
CTIX.IndicatorDetails.analyst_tlp string Analyst provided TLP on the indicator
CTIX.IndicatorDetails.asn string ASN of the indicator
CTIX.IndicatorDetails.attribute_field string Attribute field of the indicator
CTIX.IndicatorDetails.attribute_value string Attribute value of the indicator
CTIX.IndicatorDetails.base_type string Base type of the indicator
CTIX.IndicatorDetails.confidence_score number Confidence score of the IOC
CTIX.IndicatorDetails.confidence_type string Confidence type of the IOC
CTIX.IndicatorDetails.country string Country of origin of the IOC
CTIX.IndicatorDetails.created number Timestamp of when the indicator was created
CTIX.IndicatorDetails.ctix_created number Timestamp of when the indicator was created in CTIX
CTIX.IndicatorDetails.ctix_modified number Timestamp of when the indicator was modified in CTIX
CTIX.IndicatorDetails.ctix_score number CTIX score of the indicator
CTIX.IndicatorDetails.ctix_tlp string CTIX assigned TLP of the indicator
CTIX.IndicatorDetails.defang_analyst_description string Defanged analyst description of the indicator
CTIX.IndicatorDetails.description string Description of the indicator
CTIX.IndicatorDetails.fang_analyst_description string Fang analyst description of the indicator
CTIX.IndicatorDetails.first_seen number Timestamp of then the indicator was first seen
CTIX.IndicatorDetails.last_seen number Timestamp of then the indicator was last seen
CTIX.IndicatorDetails.modified number Timestamp of then the indicator was modified
CTIX.IndicatorDetails.name string Name of the indicator
CTIX.IndicatorDetails.pattern string STIX pattern of the indicator
CTIX.IndicatorDetails.pattern_type string Pattern type of the indicator
CTIX.IndicatorDetails.pattern_version string STIX pattern version
CTIX.IndicatorDetails.sources unknown Sources of the indicator
CTIX.IndicatorDetails.sub_type string Sub type of the indicator
CTIX.IndicatorDetails.tld string TLD of the indicator
CTIX.IndicatorDetails.tlp string TLP of the indicator
CTIX.IndicatorDetails.type string Type of the indicator
CTIX.IndicatorDetails.types string Types of the indicator
CTIX.IndicatorDetails.valid_from number Timestamp of the indicator from then it was valid
CTIX.IndicatorDetails.valid_until unknown Timestamp of the indicator until when it is valid

Command Example

!ctix-get-indicator-details object_id=20067ec2-8ad1-470e-b0bb-3c4a72b15883 object_type=indicator

Context Example

{"aliases": null, "analyst_description": null, "analyst_score": null,
 "analyst_tlp": null, "asn": null, "attribute_field": "value",
 "attribute_value": "x.x.x.x", "base_type": "sdo", "confidence_score":
 18, "confidence_type": "CTIX", "country": "Netherlands", "created":
 1651648700, "ctix_created": 1651648700, "ctix_modified": 1652113922,
 "ctix_score": 18, "ctix_tlp": null, "defang_analyst_description": null,
 "description": null, "fang_analyst_description": null, "first_seen": null,
 "last_seen": null, "modified": 1651648700, "name": "x.x.x.x",
 "pattern": "[ipv4-addr:value = x.x.x.x]", "pattern_type": "stix",
 "pattern_version": "2.1", "sources": [{"id":
 "e941f6fb-387b-452c-b77d-b5b05c5e9df2", "name": "Dummy",
 "source_type": "API_FEEDS"}], "sub_type": "ipv4-addr", "tld": "", "tlp":
 "WHITE", "type": "indicator", "types": ["anomalous-activity"], "valid_from":
 1644335851, "valid_until": null}

ctix-get-indicator-tags


Get indicator tags

Base Command

ctix-get-indicator-tags

Input

Argument Name Description Required
object_id Object ID of the indicator. Optional
object_type Object type of the indicator. Optional
page Page number for pagination. Default is 1. Optional
page_size Page size for pagination. Default is 10. Optional

Context Output

Path Type Description
CTIX.IndicatorTags.notes unknown Notes on the indicator’s tag
CTIX.IndicatorTags.is_deprecated boolean If the indicator’s tag deprecated or not
CTIX.IndicatorTags.is_revoked boolean If the indicator’s tag revoked or not
CTIX.IndicatorTags.ctix_created number Timestamp of when the Indicator tag was created in CTIX
CTIX.IndicatorTags.is_false_positive boolean If the indicator’s tag is false positive or not
CTIX.IndicatorTags.name string Name of the indicator
CTIX.IndicatorTags.is_reviewed boolean If the indicator reviewed or not
CTIX.IndicatorTags.is_whitelisted boolean If the indicator whitelisted or not
CTIX.IndicatorTags.is_under_review boolean If the indicator is under review or not
CTIX.IndicatorTags.is_watchlist boolean If the indicator is under watchlist or not
CTIX.IndicatorTags.tags unknown Tags of the indicator
CTIX.IndicatorTags.sub_type unknown Sub type of the indicator
CTIX.IndicatorTags.type unknown Type of Indicator

Command Example

!ctix-get-indicator-tags object_id=20067ec2-8ad1-470e-b0bb-3c4a72b15883 object_type=indicator

Context Example

{
    "notes": [],
    "is_deprecated": false,
    "is_revoked": false,
    "ctix_created": 1651648700,
    "is_false_positive": false,
    "name": "x.x.x.x",
    "is_reviewed": false,
    "is_whitelisted": false,
    "is_under_review": false,
    "is_watchlist": false,
    "tags": [
        {
            "colour_code": null,
            "id": "e2139fd5-fe05-48c5-8aaf-a5dfce900919",
            "name": "test crowd"
        },
        {
            "colour_code": null,
            "id": "fb22e904-ad74-4b6e-987e-46e81caec9ed",
            "name": "MaliciousConfidence/Low"
        }
    ],
    "sub_type": "ipv4-addr",
    "type": "indicator"
}

ctix-get-object-relations


Get Object relations

Base Command

ctix-get-object-relations

Input

Argument Name Description Required
page Page number for pagination. Default is 1. Optional
page_size Page size for pagination. Default is 10. Optional
object_id Object ID of the indicator. Optional
object_type Object type of the indicator. Optional

Context Output

Path Type Description
CTIX.IndicatorRelations.relationship_type unknown Indicator relation types
CTIX.IndicatorRelations.sources unknown Indicator sources
CTIX.IndicatorRelations.target_ref unknown Indicator target reference

Command Example

!ctix-get-object-relations object_id=20067ec2-8ad1-470e-b0bb-3c4a72b15883 object_type=indicator

Context Example

{
    "next": null,
    "page_size": 10,
    "previous": null,
    "results": [
        {
            "relationship_type": "related-to",
            "sources": [
                {
                    "id": "48e5966e-5d1b-4cf9-8e79-306aa8702a28",
                    "name": "dummy",
                    "source_type": "RSS_FEED"
                }
            ],
            "target_ref": {
                "created": 1652081903,
                "id": "cb728d0e-3e31-4c3d-8f7d-09726a8bf7a8",
                "modified": 1652081903,
                "name": "Feed 6",
                "object_type": "report",
                "sub_type": null,
                "tlp": "AMBER"
            }
        }
    ],
    "total": 1
}

ctix-get-indicator-observations


Get indicator observations

Base Command

ctix-get-indicator-observations

Input

Argument Name Description Required
page Page number for pagination. Default is 1. Optional
page_size Page size for pagination. Default is 10. Optional
object_id Object ID of the indicator. Optional
object_type Object type of the indicator. Optional

Context Output

Path Type Description
CTIX.IndicatorObservations.custom_attributes unknown Custom attributes if any
CTIX.IndicatorObservations.ctix_modified number Timestamp when indicator was modified in CTIX
CTIX.IndicatorObservations.created number Timestamp when indicator was created
CTIX.IndicatorObservations.pattern_type string Pattern type of Indicator
CTIX.IndicatorObservations.modified number Timestamp when indicator was modified
CTIX.IndicatorObservations.ctix_created number Timestamp when indicator was created in CTIX
CTIX.IndicatorObservations.pattern_version string STIX Pattern version of indicator
CTIX.IndicatorObservations.confidence string Confidence level of the indicator
CTIX.IndicatorObservations.valid_from number Timestamp when indicator was valid from
CTIX.IndicatorObservations.pattern string STIX pattern
CTIX.IndicatorObservations.fang_description string FANG description
CTIX.IndicatorObservations.defang_description string DEFANG description
CTIX.IndicatorObservations.spec_version string STIX Spec version
CTIX.IndicatorObservations.tags unknown Tags attached to the indicator
CTIX.IndicatorObservations.received_id string STIX ID when indicator was received
CTIX.IndicatorObservations.types unknown STIX Types attached to the indicator
CTIX.IndicatorObservations.source unknown STIX source of the indicator
CTIX.IndicatorObservations.id string ID of the indicator
CTIX.IndicatorObservations.valid_until number Timestamp till when the indicator is valid
CTIX.IndicatorObservations.sco_object_id unknown SCO object ID
CTIX.IndicatorObservations.unique_hash unknown Unique hash of the indicator
CTIX.IndicatorObservations.description unknown Description of the indicator
CTIX.IndicatorObservations.granular_markings unknown Granular Markings if any
CTIX.IndicatorObservations.collection unknown Collection details of the indicator

Command Example

!ctix-get-indicator-observations object_id=20067ec2-8ad1-470e-b0bb-3c4a72b15883 object_type=indicator

Context Example

{
 "result": {
  "next": null,
  "page_size": 10,
  "previous": null,
  "results": [
   {
    "custom_attributes": [],
    "ctix_modified": 1651648700,
    "created": 1644335851,
    "pattern_type": "stix",
    "modified": 1651648700,
    "ctix_created": 1651648700,
    "pattern_version": "2.1",
    "confidence": "LOW",
    "valid_from": 1644335851,
    "pattern": "[ipv4-addr:value = 'x.x.x.x']",
    "fang_description": null,
    "defang_description": null,
    "spec_version": "2.1",
    "tags": [
     {
      "colour_code": null,
      "id": "e2139fd5-fe05-48c5-8aaf-a5dfce900919",
      "name": "test crowd"
     },
     {
      "colour_code": null,
      "id": "fb22e904-ad74-4b6e-987e-46e81caec9ed",
      "name": "MaliciousConfidence/Low"
     }
    ],
    "received_id": "indicator--16a66ac2-3524-44a6-9b9d-5bec6bc80d91",
    "types": [
     "anomalous-activity"
    ],
    "source": {
     "id": "e941f6fb-387b-452c-b77d-b5b05c5e9df2",
     "name": "Dummy",
     "source_type": "API_FEEDS"
    },
    "id": "0a11d417-3501-4230-8454-c70e700cf1b8",
    "valid_until": null,
    "sco_object_id": "20067ec2-8ad1-470e-b0bb-3c4a72b15883",
    "unique_hash": "babea09af794cc5ae1403302e9ec5c2d",
    "description": "None",
    "granular_markings": [],
    "collection": {
     "id": "3d7df0f3-8c88-43d2-8742-deee21eb6ee0",
     "name": "test-crowd-ip"
    }
   }
  ],
  "total": 1
 }
}

ctix-get-conversion-feed-source


Gets the Source Details of an API feed in Cyware Intel Exchange

Base Command

ctix-get-conversion-feed-source

Input

Argument Name Description Required
page Page number for pagination. Default is 1. Optional
page_size Page size for pagination. Default is 10. Optional
object_id Object ID of the indicator. Optional
object_type Object type of the indicator. Optional
q Search query to filter feed sources. Optional

Context Output

Path Type Description
CTIX.ConversionFeedSource.created number Indicator creation timestamp
CTIX.ConversionFeedSource.id string ID of the Indicator Source
CTIX.ConversionFeedSource.name string Name of the Indicator Source
CTIX.ConversionFeedSource.taxii_option string TAXII option

Command Example

!ctix-get-conversion-feed-source object_id=20067ec2-8ad1-470e-b0bb-3c4a72b15883 object_type=indicator

Context Example

{
 "result": {
  "next": "feed-sources/?page=2&page_size=10&object_id=1ff2a18a-0574-4015-bbec-bc7692dccb14&object_type=indicator",
  "page_size": 10,
  "previous": null,
  "results": [
   {
    "created": 1651841206,
    "id": "9c82a682-254f-410d-a1c0-dc3514415f79",
    "name": "dummy-threatmailbox",
    "taxii_option": "2.1"
   }
  ],
  "total": 31
 }
}

ctix-get-lookup-threat-data


Lookup to get threat data

Base Command

ctix-get-lookup-threat-data

Input

Argument Name Description Required
object_type Object type of the indicator. Optional
ioc_type IOC type filter (e.g. ipv4-addr, domain-name, url, MD5). Can be a comma-separated list. Optional
object_names Will contain the SDO values. For example: If you need to get the object_ids of indicator 127.0.0.1 then the value will be 127.0.0.1. Optional
page_size Page size for pagination. Default is 10. Optional

Context Output

Path Type Description
CTIX.ThreatDataLookup.analyst_score number Analyst score of the indicator
CTIX.ThreatDataLookup.analyst_tlp string Analyst TLP of the indicator
CTIX.ThreatDataLookup.confidence_score number Confidence score of the indicator
CTIX.ThreatDataLookup.confidence_type string Confidence type of the indicator
CTIX.ThreatDataLookup.country string Indicator origin country
CTIX.ThreatDataLookup.created number Timestamp of when the indicator was created
CTIX.ThreatDataLookup.ctix_created number Timestamp of when the indicator was created in CTIX
CTIX.ThreatDataLookup.ctix_modified number Timestamp of when the indicator was modified in CTIX
CTIX.ThreatDataLookup.first_seen number Timestamp of when the indicator was first seen
CTIX.ThreatDataLookup.id string Object ID of the Indicator
CTIX.ThreatDataLookup.indicator_type string Indicator type
CTIX.ThreatDataLookup.ioc_type string IOC type
CTIX.ThreatDataLookup.is_actioned boolean Indicates if the indicator has been actioned
CTIX.ThreatDataLookup.is_deprecated boolean Indicates if the indicator is deprecated
CTIX.ThreatDataLookup.is_false_positive boolean Indicates if the indicator is a false positive
CTIX.ThreatDataLookup.is_reviewed boolean Indicates if the indicator has been reviewed
CTIX.ThreatDataLookup.is_revoked boolean Indicates if the indicator has been revoked
CTIX.ThreatDataLookup.is_watchlist boolean Indicates if the indicator is watchlisted
CTIX.ThreatDataLookup.is_whitelisted boolean Indicates if the indicator is whitelisted
CTIX.ThreatDataLookup.last_seen number Timestamp of when the indicator was last seen
CTIX.ThreatDataLookup.modified number Timestamp of when the indicator was modified
CTIX.ThreatDataLookup.name string Name of the indicator
CTIX.ThreatDataLookup.null unknown null
CTIX.ThreatDataLookup.primary_attribute string Details of the Primary Attribute
CTIX.ThreatDataLookup.published_collections unknown Published collections
CTIX.ThreatDataLookup.risk_severity string Risk severity
CTIX.ThreatDataLookup.source_collections unknown Source collections
CTIX.ThreatDataLookup.source_confidence string Source confidence
CTIX.ThreatDataLookup.sources unknown Sources
CTIX.ThreatDataLookup.sub_type string Sub type
CTIX.ThreatDataLookup.subscriber_collections unknown subscriber collections
CTIX.ThreatDataLookup.subscribers unknown subscribers
CTIX.ThreatDataLookup.tags unknown Tags
CTIX.ThreatDataLookup.tlp string TLP
CTIX.ThreatDataLookup.type string Type
CTIX.ThreatDataLookup.valid_from number Timestamp from when the indicator was valid
CTIX.ThreatDataLookup.valid_until number Timestamp till when the indicator was valid

Command example

!ctix-get-lookup-threat-data object_names=example.com,3.4.5.6 object_type=indicator

Context Example

{
    "CTIX": {
        "ThreatDataLookup": {
            "analyst_cvss_score": null,
            "analyst_score": null,
            "analyst_tlp": null,
            "confidence_score": 100,
            "confidence_type": "ctix",
            "country": null,
            "created": 1674080000,
            "ctix_created": 1674080000,
            "ctix_modified": 1674080000,
            "custom_attributes": [],
            "first_seen": null,
            "id": "6779a969-6404-4dd7-97ef-dec877c03c4f",
            "indicator_type": "domain-name",
            "ioc_type": "domain-name",
            "is_actioned": false,
            "is_deprecated": false,
            "is_false_positive": false,
            "is_reviewed": false,
            "is_revoked": false,
            "is_watchlist": false,
            "is_whitelisted": false,
            "last_seen": null,
            "modified": 1674080001,
            "name": "example.com",
            "null": [],
            "primary_attribute": null,
            "published_collections": [],
            "risk_severity": null,
            "severity": "UNKNOWN",
            "source_collections": [
                {
                    "id": "a9d67cc1-5de8-460b-8bf4-63abc7ceaa54",
                    "name": "anotherone (OpenAPI)"
                }
            ],
            "source_confidence": "HIGH",
            "sources": [
                {
                    "id": "38102b0e-1af4-4ee2-a62e-dd5f2ffaff5a",
                    "name": "testing (OpenAPI)",
                    "source_type": "MISCELLANEOUS"
                }
            ],
            "sub_type": "value",
            "subscriber_collections": [],
            "subscribers": [],
            "tags": [
                {
                    "colour_code": "#5236E2",
                    "id": "9635c41b-80fb-4a98-a1f3-e5796c72bb29",
                    "name": "created_using_openapi_lookup"
                }
            ],
            "tlp": "AMBER",
            "type": "indicator",
            "valid_from": 1674080000,
            "valid_until": null
        }
    },
    "DBotScore": {
        "Indicator": "example.com",
        "Reliability": "C - Fairly reliable",
        "Score": 3,
        "Type": "domain",
        "Vendor": "Cyware Intel Exchange"
    },
    "Domain": {
        "Malicious": {
            "Description": null,
            "Vendor": "Cyware Intel Exchange"
        },
        "Name": "example.com"
    }
}

Human Readable Output

Lookup Data

|confidence_score|confidence_type|created|ctix_created|ctix_modified|id|indicator_type|ioc_type|is_actioned|is_deprecated|is_false_positive|is_reviewed|is_revoked|is_watchlist|is_whitelisted|modified|name|severity|source_collections|source_confidence|sources|sub_type|tags|tlp|type|valid_from|
|—|—|—|—|—|—|—|—|—|—|—|—|—|—|—|—|—|—|—|—|—|—|—|—|—|—|

31 ctix 1666709826 1666874647 1670548277 10104a10-74a9-45d7-a412-f11531d64a38 domain-name domain-name false false false false false false false 1667442806 example.com UNKNOWN {‘id’: ‘2a5a9989-030d-466b-b676-223d2b1f4d1e’, ‘name’: ‘Indicators v4’}, {‘id’: ‘5f4230a4-cc3a-4d32-b3ee-c53a373e2a8f’, ‘name’: ‘https://www.example.com/index.xml’}, {‘id’: ‘2dc18ee7-ee80-4fa7-953d-4df824f8e8ce’, ‘name’: ‘https://www.example.com/index.xml’} MEDIUM {‘id’: ‘131392bb-ecdf-45ae-8f22-b1160cf03401’, ‘name’: ‘Mandiant Threat Intelligence’, ‘source_type’: ‘API_FEEDS’}, {‘id’: ‘87e622e3-e8e5-4692-9b79-00efead3f874’, ‘name’: ‘https://www.example.com/index.xml’, ‘source_type’: ‘RSS_FEED’}, {‘id’: ‘0647eb19-c559-4d27-a441-b70117315e18’, ‘name’: ‘https://www.example.com/index.xml’, ‘source_type’: ‘RSS_FEED’} value AMBER indicator 1530174464

ctix-get-create-threat-data


Get or create threat data

Base Command

ctix-get-create-threat-data

Input

Argument Name Description Required
object_names Will contain the SDO values. For example: If you need to get the object_ids of indicator 127.0.0.1 then the value will be 127.0.0.1. Required
page_size Page size for pagination. Default value is 10. Optional
source The source of the threat data. Optional
collection The collection to store the threat data in. Optional

Context Output

Path Type Description
CTIX.ThreatDataGetCreate.Found.analyst_score number Analyst score of the indicator
CTIX.ThreatDataGetCreate.Found.analyst_tlp string Analyst TLP of the indicator
CTIX.ThreatDataGetCreate.Found.confidence_score number Confidence score of the indicator
CTIX.ThreatDataGetCreate.Found.confidence_type string Confidence type of the indicator
CTIX.ThreatDataGetCreate.Found.country string Indicator origin country
CTIX.ThreatDataGetCreate.Found.created number Timestamp of when the indicator was created
CTIX.ThreatDataGetCreate.Found.ctix_created number Timestamp of when the indicator was created in CTIX
CTIX.ThreatDataGetCreate.Found.ctix_modified number Timestamp of when the indicator was modified in CTIX
CTIX.ThreatDataGetCreate.Found.first_seen number Timestamp of when the indicator was first seen
CTIX.ThreatDataGetCreate.Found.id string Indicator ID
CTIX.ThreatDataGetCreate.Found.indicator_type string Indicator type
CTIX.ThreatDataGetCreate.Found.ioc_type string IOC type
CTIX.ThreatDataGetCreate.Found.is_actioned boolean Is actioned
CTIX.ThreatDataGetCreate.Found.is_deprecated boolean is deprecated
CTIX.ThreatDataGetCreate.Found.is_false_positive boolean is false positive
CTIX.ThreatDataGetCreate.Found.is_reviewed boolean is reviewed
CTIX.ThreatDataGetCreate.Found.is_revoked boolean is revoked
CTIX.ThreatDataGetCreate.Found.is_watchlist boolean is watchlisted
CTIX.ThreatDataGetCreate.Found.is_whitelisted boolean is allowed
CTIX.ThreatDataGetCreate.Found.last_seen number Timestamp of when the indicator was last seen
CTIX.ThreatDataGetCreate.Found.modified number Timestamp of when the indicator was modified
CTIX.ThreatDataGetCreate.Found.name string name of the indicator
CTIX.ThreatDataGetCreate.Found.null unknown null
CTIX.ThreatDataGetCreate.Found.primary_attribute string Primary Attribute
CTIX.ThreatDataGetCreate.Found.published_collections unknown published collections
CTIX.ThreatDataGetCreate.Found.risk_severity string Risk severity
CTIX.ThreatDataGetCreate.Found.source_collections unknown sources collections
CTIX.ThreatDataGetCreate.Found.source_confidence string Source confidence
CTIX.ThreatDataGetCreate.Found.sources unknown sources
CTIX.ThreatDataGetCreate.Found.sub_type string Sub type
CTIX.ThreatDataGetCreate.Found.subscriber_collections unknown subscriber collections
CTIX.ThreatDataGetCreate.Found.subscribers unknown subscribers
CTIX.ThreatDataGetCreate.Found.tags unknown Tags
CTIX.ThreatDataGetCreate.Found.tlp string TLP
CTIX.ThreatDataGetCreate.Found.type string Type
CTIX.ThreatDataGetCreate.Found.valid_from number Timestamp from when the indicator was valid
CTIX.ThreatDataGetCreate.Found.valid_until number Timestamp till when the indicator was valid
CTIX.ThreatDataGetCreate.NotFoundCreated string IOCs that weren’t found, and therefore were created
CTIX.ThreatDataGetCreate.NotFoundInvalid string IOCs that were found to be invalid, so they were not created

Command example

!ctix-get-create-threat-data object_names=example.com,x.x.x.x,zzzzz collection=some_collection source=some_source

Context Example

{
    "CTIX": {
        "ThreatDataGetCreate": {
            "Found": {
                "analyst_cvss_score": null,
                "analyst_score": null,
                "analyst_tlp": null,
                "confidence_score": 100,
                "confidence_type": "ctix",
                "country": null,
                "created": 1674080000,
                "ctix_created": 1674080000,
                "ctix_modified": 1674080000,
                "custom_attributes": [],
                "first_seen": null,
                "id": "6779a969-6404-4dd7-97ef-dec877c03c4f",
                "indicator_type": "domain-name",
                "ioc_type": "domain-name",
                "is_actioned": false,
                "is_deprecated": false,
                "is_false_positive": false,
                "is_reviewed": false,
                "is_revoked": false,
                "is_watchlist": false,
                "is_whitelisted": false,
                "last_seen": null,
                "modified": 1674080001,
                "name": "example.com",
                "null": [],
                "primary_attribute": null,
                "published_collections": [],
                "risk_severity": null,
                "severity": "UNKNOWN",
                "source_collections": [
                    {
                        "id": "a9d67cc1-5de8-460b-8bf4-63abc7ceaa54",
                        "name": "anotherone (OpenAPI)"
                    }
                ],
                "source_confidence": "HIGH",
                "sources": [
                    {
                        "id": "38102b0e-1af4-4ee2-a62e-dd5f2ffaff5a",
                        "name": "testing (OpenAPI)",
                        "source_type": "MISCELLANEOUS"
                    }
                ],
                "sub_type": "value",
                "subscriber_collections": [],
                "subscribers": [],
                "tags": [
                    {
                        "colour_code": "#5236E2",
                        "id": "9635c41b-80fb-4a98-a1f3-e5796c72bb29",
                        "name": "created_using_openapi_lookup"
                    }
                ],
                "tlp": "AMBER",
                "type": "indicator",
                "valid_from": 1674080000,
                "valid_until": null
            },
            "NotFoundCreated": [
                "x.x.x.x"
            ],
            "NotFoundInvalid": [
                "zzzzz"
            ]
        }
    },
    "DBotScore": {
        "Indicator": "example.com",
        "Reliability": "C - Fairly reliable",
        "Score": 3,
        "Type": "domain",
        "Vendor": "Cyware Intel Exchange"
    },
    "Domain": {
        "Malicious": {
            "Description": null,
            "Vendor": "Cyware Intel Exchange"
        },
        "Name": "example.com"
    }
}

Human Readable Output

Not Found: Invalid

Name
zzzzz

domain


Lookup domain threat data

Notice: Using this command to submit indicators may make the data publicly available. Refer to the vendor’s documentation for more details.

Base Command

domain

Input

Argument Name Description Required
domain Will contain domain SDO values. For example: If you need to get the object_ids of indicator example.com then the value will be example.com. Required

Context Output

Path Type Description
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.
Domain.Name String The domain name. For example: “google.com”.

Command example

!domain domain="example.com" using="Cyware Intel Exchange_instance"

Context Example

{
    "CTIX": {
        "ThreatDataLookup": {
            "Found": {
                "analyst_score": null,
                "analyst_tlp": null,
                "confidence_score": 31,
                "confidence_type": "ctix",
                "country": null,
                "created": 1666709826,
                "ctix_created": 1666874647,
                "ctix_modified": 1670548277,
                "first_seen": null,
                "id": "10104a10-74a9-45d7-a412-f11531d64a38",
                "indicator_type": "domain-name",
                "ioc_type": "domain-name",
                "is_actioned": false,
                "is_deprecated": false,
                "is_false_positive": false,
                "is_reviewed": false,
                "is_revoked": false,
                "is_watchlist": false,
                "is_whitelisted": false,
                "last_seen": null,
                "modified": 1667442806,
                "name": "example.com",
                "null": [],
                "primary_attribute": null,
                "published_collections": [],
                "risk_severity": "UNKNOWN",
                "source_collections": [
                    {
                        "id": "2a5a9989-030d-466b-b676-223d2b1f4d1e",
                        "name": "Indicators v4"
                    },
                    {
                        "id": "5f4230a4-cc3a-4d32-b3ee-c53a373e2a8f",
                        "name": "https://www.example.com/index.xml"
                    },
                    {
                        "id": "2dc18ee7-ee80-4fa7-953d-4df824f8e8ce",
                        "name": "https://www.example.com/index.xml"
                    }
                ],
                "source_confidence": "MEDIUM",
                "sources": [
                    {
                        "id": "131392bb-ecdf-45ae-8f22-b1160cf03401",
                        "name": "Mandiant Threat Intelligence",
                        "source_type": "API_FEEDS"
                    },
                    {
                        "id": "87e622e3-e8e5-4692-9b79-00efead3f874",
                        "name": "https://www.example.com/index.xml",
                        "source_type": "RSS_FEED"
                    },
                    {
                        "id": "0647eb19-c559-4d27-a441-b70117315e18",
                        "name": "https://www.example.com/index.xml",
                        "source_type": "RSS_FEED"
                    }
                ],
                "sub_type": "value",
                "subscriber_collections": [],
                "subscribers": [],
                "tags": [],
                "tlp": "AMBER",
                "type": "indicator",
                "valid_from": 1530174464,
                "valid_until": null
            }
        }
    },
    "DBotScore": {
        "Indicator": "example.com",
        "Reliability": "C - Fairly reliable",
        "Score": 2,
        "Type": "domain",
        "Vendor": "Cyware Intel Exchange"
    },
    "Domain": {
        "Name": "example.com"
    }
}

Human Readable Output

Lookup Data

confidence_score confidence_type created ctix_created ctix_modified id indicator_type ioc_type is_actioned is_deprecated is_false_positive is_reviewed is_revoked is_watchlist is_whitelisted modified name risk_severity source_collections source_confidence sources sub_type tlp type valid_from
31 ctix 1666709826 1666874647 1670548277 10104a10-74a9-45d7-a412-f11531d64a38 domain-name domain-name false false false false false false false 1667442806 example.com UNKNOWN {‘id’: ‘2a5a9989-030d-466b-b676-223d2b1f4d1e’, ‘name’: ‘Indicators v4’},, {‘id’: ‘5f4230a4-cc3a-4d32-b3ee-c53a373e2a8f’, ‘name’: ‘https://www.example.com/index.xml’},, {‘id’: ‘2dc18ee7-ee80-4fa7-953d-4df824f8e8ce’, ‘name’: ‘https://www.example.com/index.xml’} MEDIUM {‘id’: ‘131392bb-ecdf-45ae-8f22-b1160cf03401’, ‘name’: ‘Mandiant Threat Intelligence’, ‘source_type’: ‘API_FEEDS’},, {‘id’: ‘87e622e3-e8e5-4692-9b79-00efead3f874’, ‘name’: ‘https://www.example.com/index.xml’, ‘source_type’: ‘RSS_FEED’},, {‘id’: ‘0647eb19-c559-4d27-a441-b70117315e18’, ‘name’: ‘https://www.example.com/index.xml’, ‘source_type’: ‘RSS_FEED’} value AMBER indicator 1530174464

ip


Lookup IP threat data.

Base Command

ip

Input

Argument Name Description Required
ip Will contain IP SDO values. For example: If you need to get the object_ids of indicator 1.2.3.4 then the value will be 1.2.3.4. Required

Context Output

Path Type Description
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 provided by the vendor.
IP.Address String The IP address. For example: 1.2.3.4.

Command example

!ip ip="x.x.x.x" using="Cyware Intel Exchange_instance"

Context Example

{
    "CTIX": {
        "ThreatDataLookup": {
            "Found": {
                "analyst_score": null,
                "analyst_tlp": null,
                "confidence_score": 100,
                "confidence_type": "ctix",
                "country": "United States",
                "created": 1666710084,
                "ctix_created": 1666874647,
                "ctix_modified": 1671604244,
                "first_seen": null,
                "id": "5c2517a2-759f-4eb8-b9fa-346ff20cfaaf",
                "indicator_type": "ipv4-addr",
                "ioc_type": "ipv4-addr",
                "is_actioned": false,
                "is_deprecated": false,
                "is_false_positive": false,
                "is_reviewed": false,
                "is_revoked": false,
                "is_watchlist": false,
                "is_whitelisted": false,
                "last_seen": null,
                "modified": 1669170873,
                "name": "x.x.x.x",
                "null": [],
                "primary_attribute": null,
                "published_collections": [],
                "risk_severity": "UNKNOWN",
                "source_collections": [
                    {
                        "id": "2a5a9989-030d-466b-b676-223d2b1f4d1e",
                        "name": "Indicators v4"
                    },
                    {
                        "id": "fe150b23-6354-4a9b-8c27-202abc758ba3",
                        "name": "NCAS JG Test"
                    }
                ],
                "source_confidence": "HIGH",
                "sources": [
                    {
                        "id": "131392bb-ecdf-45ae-8f22-b1160cf03401",
                        "name": "Mandiant Threat Intelligence",
                        "source_type": "API_FEEDS"
                    },
                    {
                        "id": "50cbaaee-8083-494c-b42a-7c7fb73ca2dc",
                        "name": "NCAS JG Test",
                        "source_type": "RSS_FEED"
                    }
                ],
                "sub_type": "value",
                "subscriber_collections": [],
                "subscribers": [],
                "tags": [
                    {
                        "colour_code": "#5236E2",
                        "id": "f82fa004-75cc-4824-b129-914ec13728b5",
                        "name": "Destruction"
                    }
                ],
                "tlp": "AMBER",
                "type": "indicator",
                "valid_from": 1409607591,
                "valid_until": null
            }
        }
    },
    "DBotScore": {
        "Indicator": "x.x.x.x",
        "Reliability": "C - Fairly reliable",
        "Score": 3,
        "Type": "ip",
        "Vendor": "Cyware Intel Exchange"
    },
    "IP": {
        "Address": "x.x.x.x",
        "Malicious": {
            "Description": null,
            "Vendor": "Cyware Intel Exchange"
        }
    }
}

Human Readable Output

Lookup Data

|confidence_score|confidence_type|country|created|ctix_created|ctix_modified|id|indicator_type|ioc_type|is_actioned|is_deprecated|is_false_positive|is_reviewed|is_revoked|is_watchlist|is_whitelisted|modified|name|risk_severity|source_collections|source_confidence|sources|sub_type|tags|tlp|type|valid_from|
|—|—|—|—|—|—|—|—|—|—|—|—|—|—|—|—|—|—|—|—|—|—|—|—|—|—|—|

100 ctix United States 1666710084 1666874647 1671604244 5c2517a2-759f-4eb8-b9fa-346ff20cfaaf ipv4-addr ipv4-addr false false false false false false false 1669170873 x.x.x.x UNKNOWN {‘id’: ‘2a5a9989-030d-466b-b676-223d2b1f4d1e’, ‘name’: ‘Indicators v4’}, {‘id’: ‘fe150b23-6354-4a9b-8c27-202abc758ba3’, ‘name’: ‘NCAS JG Test’} HIGH {‘id’: ‘131392bb-ecdf-45ae-8f22-b1160cf03401’, ‘name’: ‘Mandiant Threat Intelligence’, ‘source_type’: ‘API_FEEDS’}, {‘id’: ‘50cbaaee-8083-494c-b42a-7c7fb73ca2dc’, ‘name’: ‘NCAS JG Test’, ‘source_type’: ‘RSS_FEED’} value {‘colour_code’: ‘#5236E2’, ‘id’: ‘f82fa004-75cc-4824-b129-914ec13728b5’, ‘name’: ‘Destruction’} AMBER indicator 1409607591

file


Lookup file threat data

Base Command

file

Input

Argument Name Description Required
file Will contain file SDO values. For example: If you need to get the object_ids of a file hash 3ed0a30799543fa2c3a913c7985bffed then the value will be 3ed0a30799543fa2c3a913c7985bffed. Required

Context Output

Path Type Description
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.
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.

Command example

!file file="9c57753557ed258d731987834c56fa4c" using="Cyware Intel Exchange_instance"

Context Example

{
    "CTIX": {
        "ThreatDataLookup": {
            "Found": {
                "analyst_score": null,
                "analyst_tlp": null,
                "confidence_score": 100,
                "confidence_type": "ctix",
                "country": null,
                "created": 1673710318,
                "ctix_created": 1674124925,
                "ctix_modified": 1674124925,
                "first_seen": null,
                "id": "4ea5874d-0d6e-4a65-a8db-61d825d9fb8e",
                "indicator_type": "file",
                "ioc_type": "MD5",
                "is_actioned": false,
                "is_deprecated": false,
                "is_false_positive": false,
                "is_reviewed": false,
                "is_revoked": false,
                "is_watchlist": false,
                "is_whitelisted": false,
                "last_seen": null,
                "modified": 1673710318,
                "name": "9c57753557ed258d731987834c56fa4c",
                "null": [],
                "primary_attribute": null,
                "published_collections": [],
                "risk_severity": "UNKNOWN",
                "source_collections": [
                    {
                        "id": "2a5a9989-030d-466b-b676-223d2b1f4d1e",
                        "name": "Indicators v4"
                    }
                ],
                "source_confidence": "HIGH",
                "sources": [
                    {
                        "id": "131392bb-ecdf-45ae-8f22-b1160cf03401",
                        "name": "Mandiant Threat Intelligence",
                        "source_type": "API_FEEDS"
                    }
                ],
                "sub_type": "MD5",
                "subscriber_collections": [],
                "subscribers": [],
                "tags": [],
                "tlp": "AMBER",
                "type": "indicator",
                "valid_from": 1671281161,
                "valid_until": null
            }
        }
    },
    "DBotScore": {
        "Indicator": "9c57753557ed258d731987834c56fa4c",
        "Reliability": "C - Fairly reliable",
        "Score": 3,
        "Type": "file",
        "Vendor": "Cyware Intel Exchange"
    },
    "File": {
        "Hashes": [],
        "Malicious": {
            "Description": null,
            "Vendor": "Cyware Intel Exchange"
        },
        "Name": "9c57753557ed258d731987834c56fa4c"
    }
}

Human Readable Output

Lookup Data

confidence_score confidence_type created ctix_created ctix_modified id indicator_type ioc_type is_actioned is_deprecated is_false_positive is_reviewed is_revoked is_watchlist is_whitelisted modified name risk_severity source_collections source_confidence sources sub_type tlp type valid_from
100 ctix 1673710318 1674124925 1674124925 4ea5874d-0d6e-4a65-a8db-61d825d9fb8e file MD5 false false false false false false false 1673710318 9c57753557ed258d731987834c56fa4c UNKNOWN {‘id’: ‘2a5a9989-030d-466b-b676-223d2b1f4d1e’, ‘name’: ‘Indicators v4’} HIGH {‘id’: ‘131392bb-ecdf-45ae-8f22-b1160cf03401’, ‘name’: ‘Mandiant Threat Intelligence’, ‘source_type’: ‘API_FEEDS’} MD5 AMBER indicator 1671281161

url


Lookup URL threat data

Notice: Using this command to submit indicators may make the data publicly available. Refer to the vendor’s documentation for more details.

Base Command

url

Input

Argument Name Description Required
url Will contain URL SDO values. For example: If you need to get the object_ids of a URL https://cyware.com/ then the value will be https://cyware.com/. Required

Context Output

Path Type Description
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 provided by the vendor.
URL.Data String The URL

Command example

!url url="http://example.com/" using="Cyware Intel Exchange_instance"

Context Example

{
    "CTIX": {
        "ThreatDataLookup": {
            "Found": {
                "analyst_score": null,
                "analyst_tlp": null,
                "confidence_score": 100,
                "confidence_type": "ctix",
                "country": null,
                "created": 1674166009,
                "ctix_created": 1674166009,
                "ctix_modified": 1674166009,
                "first_seen": null,
                "id": "dcada258-5fc2-4c42-b7d6-e8ffda6c5a9e",
                "indicator_type": "url",
                "ioc_type": "url",
                "is_actioned": false,
                "is_deprecated": false,
                "is_false_positive": false,
                "is_reviewed": false,
                "is_revoked": false,
                "is_watchlist": false,
                "is_whitelisted": false,
                "last_seen": null,
                "modified": 1674166010,
                "name": "http://example.com/",
                "null": [],
                "primary_attribute": null,
                "published_collections": [
                    {
                        "id": "ad842594-8faa-49fb-841e-7ff99a685718",
                        "name": null
                    }
                ],
                "risk_severity": "UNKNOWN",
                "source_collections": [
                    {
                        "id": "5432c580-e1f9-40c3-b40a-a47686dfcf22",
                        "name": "Free Text"
                    }
                ],
                "source_confidence": "HIGH",
                "sources": [
                    {
                        "id": "7eb93036-688e-4916-ab1f-fe9015c16b78",
                        "name": "Import",
                        "source_type": "CUSTOM_STIX_SOURCES"
                    }
                ],
                "sub_type": "value",
                "subscriber_collections": [],
                "subscribers": [],
                "tags": [],
                "tlp": "AMBER",
                "type": "indicator",
                "valid_from": 1674166009,
                "valid_until": null
            }
        }
    },
    "DBotScore": {
        "Indicator": "http://example.com/",
        "Reliability": "C - Fairly reliable",
        "Score": 3,
        "Type": "url",
        "Vendor": "Cyware Intel Exchange"
    },
    "URL": {
        "Data": "http://example.com/",
        "Malicious": {
            "Description": null,
            "Vendor": "Cyware Intel Exchange"
        }
    }
}

Human Readable Output

Lookup Data

confidence_score confidence_type created ctix_created ctix_modified id indicator_type ioc_type is_actioned is_deprecated is_false_positive is_reviewed is_revoked is_watchlist is_whitelisted modified name published_collections risk_severity source_collections source_confidence sources sub_type tlp type valid_from
100 ctix 1674166009 1674166009 1674166009 dcada258-5fc2-4c42-b7d6-e8ffda6c5a9e url url false false false false false false false 1674166010 http://example.com/ {‘id’: ‘ad842594-8faa-49fb-841e-7ff99a685718’, ‘name’: None} UNKNOWN {‘id’: ‘5432c580-e1f9-40c3-b40a-a47686dfcf22’, ‘name’: ‘Free Text’} HIGH {‘id’: ‘7eb93036-688e-4916-ab1f-fe9015c16b78’, ‘name’: ‘Import’, ‘source_type’: ‘CUSTOM_STIX_SOURCES’} value AMBER indicator 1674166009

ctix-get-all-notes


Get paginated list of notes

Base Command

ctix-get-all-notes

Input

Argument Name Description Required
object_id If set, retrieves only notes associated with the threat data object with ID = object_id. Optional
page Page number for pagination. Default value is 1. Optional
page_size Page size for pagination. Default value is 10. Optional

Context Output

Path Type Description
CTIX.Note.created integer The timestamp when the note was created
CTIX.Note.created_by unknown The user who created the note
CTIX.Note.created_by.email string The email of the user who created the note
CTIX.Note.created_by.first_name string The first name of the user who created the note
CTIX.Note.created_by.id string The ID of the user who created the note
CTIX.Note.created_by.last_name string The last name of the user who created the note
CTIX.Note.id string The ID of the Note
CTIX.Note.is_json boolean A flag indicating whether the Note is in JSON format
CTIX.Note.meta_data unknown Meta data for the Note
CTIX.Note.meta_data.component string The component for the Note
CTIX.Note.modified integer The timestamp when the Note was last modified
CTIX.Note.modified_by unknown The user who last modified the Note
CTIX.Note.modified_by.email string The email of the user who last modified the Note
CTIX.Note.modified_by.first_name string The first name of the user who last modified the Note
CTIX.Note.modified_by.id string The ID of the user who last modified the Note
CTIX.Note.modified_by.last_name string The last name of the user who last modified the Note
CTIX.Note.object_id string The object ID of the Note
CTIX.Note.text string The text of the Note
CTIX.Note.title string The title of the Note
CTIX.Note.type string The type of the Note

Command example

!ctix-get-all-notes page_size=1

Context Example

{
    "CTIX": {
        "Note": {
            "created": 1674173772,
            "created_by": {
                "email": "some.user@example.com",
                "first_name": "some",
                "id": "5b03c17e-a1f8-43ab-b0d5-9e178fb95c4a",
                "last_name": "user"
            },
            "id": "f8f67182-bf72-47df-9a90-31b2bd829a9d",
            "is_json": false,
            "meta_data": {
                "component": "threatdata",
                "object_id": "ba82b524-15b3-4071-8008-e58754f8d134",
                "type": "indicator"
            },
            "modified": 1674173772,
            "modified_by": {
                "email": "some.user@example.com",
                "first_name": "some",
                "id": "5b03c17e-a1f8-43ab-b0d5-9e178fb95c4a",
                "last_name": "user"
            },
            "object_id": "ba82b524-15b3-4071-8008-e58754f8d134",
            "text": "this is the old text",
            "title": null,
            "type": "threatdata"
        }
    }
}

Human Readable Output

Note Data

created created_by id is_json meta_data modified modified_by object_id text type
1674173772 email: some.user@example.com, first_name: some, id: 5b03c17e-a1f8-43ab-b0d5-9e178fb95c4a, last_name: user f8f67182-bf72-47df-9a90-31b2bd829a9d false component: threatdata, object_id: ba82b524-15b3-4071-8008-e58754f8d134, type: indicator 1674173772 email: some.user@example.com, first_name: some, id: 5b03c17e-a1f8-43ab-b0d5-9e178fb95c4a, last_name: user ba82b524-15b3-4071-8008-e58754f8d134 this is the old text threatdata

ctix-get-note-details


Get details of a note by ID

Base Command

ctix-get-note-details

Input

Argument Name Description Required
id The ID of the note. Required

Context Output

Path Type Description
CTIX.Note.created integer The timestamp when the note was created
CTIX.Note.created_by unknown The user who created the note
CTIX.Note.created_by.email string The email of the user who created the note
CTIX.Note.created_by.first_name string The first name of the user who created the note
CTIX.Note.created_by.id string The ID of the user who created the note
CTIX.Note.created_by.last_name string The last name of the user who created the note
CTIX.Note.id string The ID of the note
CTIX.Note.is_json boolean A flag indicating whether the note is in JSON format
CTIX.Note.meta_data unknown Meta data for the note
CTIX.Note.meta_data.component string The component for the note
CTIX.Note.modified integer The timestamp when the note was last modified
CTIX.Note.modified_by unknown The user who last modified the note
CTIX.Note.modified_by.email string The email of the user who last modified the note
CTIX.Note.modified_by.first_name string The first name of the user who last modified the note
CTIX.Note.modified_by.id string The ID of the user who last modified the note
CTIX.Note.modified_by.last_name string The last name of the user who last modified the note
CTIX.Note.object_id string The object ID of the note
CTIX.Note.text string The text of the note
CTIX.Note.title string The title of the note
CTIX.Note.type string The type of the note

Command example

!ctix-get-note-details id="7d739870-ce7d-415b-bbbf-25f4bbc6be66"

Context Example

{
    "CTIX": {
        "Note": {
            "created": 1671821868,
            "created_by": {
                "email": "some.user@example.com",
                "first_name": "some",
                "id": "5b03c17e-a1f8-43ab-b0d5-9e178fb95c4a",
                "last_name": "user"
            },
            "id": "7d739870-ce7d-415b-bbbf-25f4bbc6be66",
            "is_json": false,
            "meta_data": {
                "component": "threatdata",
                "object_id": "fake",
                "type": "indicator"
            },
            "modified": 1674173787,
            "modified_by": {
                "email": "some.user@example.com",
                "first_name": "some",
                "id": "5b03c17e-a1f8-43ab-b0d5-9e178fb95c4a",
                "last_name": "user"
            },
            "object_id": "fake",
            "text": "this is the new text",
            "title": null,
            "type": "threatdata"
        }
    }
}

Human Readable Output

Note Detail Data

created created_by id is_json meta_data modified modified_by object_id text type
1671821868 email: some.user@example.com, first_name: some, id: 5b03c17e-a1f8-43ab-b0d5-9e178fb95c4a, last_name: user 7d739870-ce7d-415b-bbbf-25f4bbc6be66 false component: threatdata, object_id: fake, type: indicator 1674173787 email: some.user@example.com, first_name: some, id: 5b03c17e-a1f8-43ab-b0d5-9e178fb95c4a, last_name: user fake this is the new text threatdata

ctix-create-note


Creates a new note from the parameter ‘text’

Base Command

ctix-create-note

Input

Argument Name Description Required
text The text that you want the note to have. Required
object_id if set, will associate note to the Threat Data object with the provided ID. Optional
object_type only required if object_id is set, used to specify the type of object object_id is. Possible values are: indicator, malware, threat-actor, vulnerability, attack-pattern, campaign, course-of-action, identity, infrastructure, intrusion-set, location, malware-analysis, observed-data, opinion, tool, report, custom-object, observable, incident, note. Optional

Context Output

Path Type Description
CTIX.Note.created integer The timestamp when the note was created
CTIX.Note.created_by unknown The user who created the note
CTIX.Note.created_by.email string The email of the user who created the note
CTIX.Note.created_by.first_name string The first name of the user who created the note
CTIX.Note.created_by.id string The ID of the user who created the note
CTIX.Note.created_by.last_name string The last name of the user who created the note
CTIX.Note.id string The ID of the note
CTIX.Note.is_json boolean A flag indicating whether the note is in JSON format
CTIX.Note.meta_data unknown Meta data for the note
CTIX.Note.meta_data.component string The component for the note
CTIX.Note.modified integer The timestamp when the note was last modified
CTIX.Note.modified_by unknown The user who last modified the note
CTIX.Note.modified_by.email string The email of the user who last modified the note
CTIX.Note.modified_by.first_name string The first name of the user who last modified the note
CTIX.Note.modified_by.id string The ID of the user who last modified the note
CTIX.Note.modified_by.last_name string The last name of the user who last modified the note
CTIX.Note.object_id string The object ID of the note
CTIX.Note.text string The text of the note
CTIX.Note.title string The title of the note
CTIX.Note.type string The type of the note

Command example

!ctix-create-note text="hello world x100"

Context Example

{
    "CTIX": {
        "Note": {
            "created": 1674173831,
            "created_by": {
                "email": "some.user@example.com",
                "first_name": "some",
                "id": "5b03c17e-a1f8-43ab-b0d5-9e178fb95c4a",
                "last_name": "user"
            },
            "id": "35ee1841-8357-43e0-b372-aff9800cdc55",
            "is_json": false,
            "meta_data": {
                "component": "notes"
            },
            "modified": 1674173831,
            "modified_by": {
                "email": "some.user@example.com",
                "first_name": "some",
                "id": "5b03c17e-a1f8-43ab-b0d5-9e178fb95c4a",
                "last_name": "user"
            },
            "object_id": null,
            "text": "hello world x100",
            "title": null,
            "type": "notes"
        }
    }
}

Human Readable Output

Created Note Data

created created_by id is_json meta_data modified modified_by text type
1674173831 email: some.user@example.com, first_name: some, id: 5b03c17e-a1f8-43ab-b0d5-9e178fb95c4a, last_name: user 35ee1841-8357-43e0-b372-aff9800cdc55 false component: notes 1674173831 email: some.user@example.com, first_name: some, id: 5b03c17e-a1f8-43ab-b0d5-9e178fb95c4a, last_name: user hello world x100 notes

Command example

!ctix-create-note text="hello world x100" object_id="da1a6268-e589-4231-a334-68fb0c2cc1e0" object_type=indicator

Context Example

{
    "CTIX": {
        "Note": {
            "created": 1674173838,
            "created_by": {
                "email": "some.user@example.com",
                "first_name": "some",
                "id": "5b03c17e-a1f8-43ab-b0d5-9e178fb95c4a",
                "last_name": "user"
            },
            "id": "e5584583-6d45-4fe8-82b4-a802007c38f0",
            "is_json": false,
            "meta_data": {
                "component": "threatdata",
                "object_id": "da1a6268-e589-4231-a334-68fb0c2cc1e0",
                "type": "indicator"
            },
            "modified": 1674173838,
            "modified_by": {
                "email": "some.user@example.com",
                "first_name": "some",
                "id": "5b03c17e-a1f8-43ab-b0d5-9e178fb95c4a",
                "last_name": "user"
            },
            "object_id": "da1a6268-e589-4231-a334-68fb0c2cc1e0",
            "text": "hello world x100",
            "title": null,
            "type": "threatdata"
        }
    }
}

Human Readable Output

Created Note Data

created created_by id is_json meta_data modified modified_by object_id text type
1674173838 email: some.user@example.com, first_name: some, id: 5b03c17e-a1f8-43ab-b0d5-9e178fb95c4a, last_name: user e5584583-6d45-4fe8-82b4-a802007c38f0 false component: threatdata, object_id: da1a6268-e589-4231-a334-68fb0c2cc1e0, type: indicator 1674173838 email: some.user@example.com, first_name: some, id: 5b03c17e-a1f8-43ab-b0d5-9e178fb95c4a, last_name: user da1a6268-e589-4231-a334-68fb0c2cc1e0 hello world x100 threatdata

ctix-update-note


Updates the note text from an existing note, as specified by its ID

Base Command

ctix-update-note

Input

Argument Name Description Required
id The ID of the note. Required
text The updated text that you want the note to have. Optional
object_id If set, will associate the note to the Threat Data object with the provided ID. Optional
object_type Only required if object_id is set, used to specify the type of object object_id is. Possible values are: indicator, malware, threat-actor, vulnerability, attack-pattern, campaign, course-of-action, identity, infrastructure, intrusion-set, location, malware-analysis, observed-data, opinion, tool, report, custom-object, observable, incident, note. Optional

Context Output

Path Type Description
CTIX.Note.created integer The timestamp when the note was created
CTIX.Note.created_by unknown The user who created the note
CTIX.Note.created_by.email string The email of the user who created the note
CTIX.Note.created_by.first_name string The first name of the user who created the note
CTIX.Note.created_by.id string The ID of the user who created the note
CTIX.Note.created_by.last_name string The last name of the user who created the note
CTIX.Note.id string The ID of the note
CTIX.Note.is_json boolean A flag indicating whether the note is in JSON format
CTIX.Note.meta_data unknown Meta data for the note
CTIX.Note.meta_data.component string The component for the note
CTIX.Note.modified integer The timestamp when the note was last modified
CTIX.Note.modified_by unknown The user who last modified the note
CTIX.Note.modified_by.email string The email of the user who last modified the note
CTIX.Note.modified_by.first_name string The first name of the user who last modified the note
CTIX.Note.modified_by.id string The ID of the user who last modified the note
CTIX.Note.modified_by.last_name string The last name of the user who last modified the note
CTIX.Note.object_id string The object ID of the note
CTIX.Note.text string The text of the note
CTIX.Note.title string The title of the note
CTIX.Note.type string The type of the note

Command example

!ctix-update-note id="7d739870-ce7d-415b-bbbf-25f4bbc6be66" text="this is a test"

Context Example

{
    "CTIX": {
        "Note": {
            "created": 1671821868,
            "created_by": {
                "email": "some.user@example.com",
                "first_name": "some",
                "id": "5b03c17e-a1f8-43ab-b0d5-9e178fb95c4a",
                "last_name": "user"
            },
            "id": "7d739870-ce7d-415b-bbbf-25f4bbc6be66",
            "is_json": false,
            "meta_data": {
                "component": "threatdata",
                "object_id": "fake",
                "type": "indicator"
            },
            "modified": 1674173815,
            "modified_by": {
                "email": "some.user@example.com",
                "first_name": "some",
                "id": "5b03c17e-a1f8-43ab-b0d5-9e178fb95c4a",
                "last_name": "user"
            },
            "object_id": "fake",
            "text": "this is a test",
            "title": null,
            "type": "threatdata"
        }
    }
}

Human Readable Output

Updated Note Data

created created_by id is_json meta_data modified modified_by object_id text type
1671821868 email: some.user@example.com, first_name: some, id: 5b03c17e-a1f8-43ab-b0d5-9e178fb95c4a, last_name: user 7d739870-ce7d-415b-bbbf-25f4bbc6be66 false component: threatdata, object_id: fake, type: indicator 1674173815 email: some.user@example.com, first_name: some, id: 5b03c17e-a1f8-43ab-b0d5-9e178fb95c4a, last_name: user fake this is a test threatdata

Command example

!ctix-update-note id="7d739870-ce7d-415b-bbbf-25f4bbc6be66" object_id="da1a6268-e589-4231-a334-68fb0c2cc1e0" object_type=indicator

Context Example

{
    "CTIX": {
        "Note": {
            "created": 1671821868,
            "created_by": {
                "email": "some.user@example.com",
                "first_name": "some",
                "id": "5b03c17e-a1f8-43ab-b0d5-9e178fb95c4a",
                "last_name": "user"
            },
            "id": "7d739870-ce7d-415b-bbbf-25f4bbc6be66",
            "is_json": false,
            "meta_data": {
                "component": "threatdata",
                "object_id": "fake",
                "type": "indicator"
            },
            "modified": 1674173824,
            "modified_by": {
                "email": "some.user@example.com",
                "first_name": "some",
                "id": "5b03c17e-a1f8-43ab-b0d5-9e178fb95c4a",
                "last_name": "user"
            },
            "object_id": "da1a6268-e589-4231-a334-68fb0c2cc1e0",
            "text": "this is a test",
            "title": null,
            "type": "threatdata"
        }
    }
}

Human Readable Output

Updated Note Data

created created_by id is_json meta_data modified modified_by object_id text type
1671821868 email: some.user@example.com, first_name: some, id: 5b03c17e-a1f8-43ab-b0d5-9e178fb95c4a, last_name: user 7d739870-ce7d-415b-bbbf-25f4bbc6be66 false component: threatdata, object_id: fake, type: indicator 1674173824 email: some.user@example.com, first_name: some, id: 5b03c17e-a1f8-43ab-b0d5-9e178fb95c4a, last_name: user da1a6268-e589-4231-a334-68fb0c2cc1e0 this is a test threatdata

ctix-delete-note


Deletes an existing note using its ID.

Base Command

ctix-delete-note

Input

Argument Name Description Required
id the ID of the note. Required

Context Output

Path Type Description
CTIX.Note.deletion.details string Returns the status of the note deletion request.

Command example

!ctix-delete-note id="7d739870-ce7d-415b-bbbf-25f4bbc6be66"

Context Example

{
    "CTIX": {
        "Note": {
            "details": "success"
        }
    }
}

Human Readable Output

Deleted Note Data

details
success

ctix-make-request


Allows you to make any API call to Cyware Intel Exchange endpoints.

Base Command

ctix-make-request

Input

Argument Name Description Required
type The HTTP method you would like to call. Possible values are: GET, POST, PUT, DELETE. Required
endpoint URL suffix of the API call to CTIX. Required
body Any data you would like to pass, in JSON format. Optional
params Any parameters you would like to pass, in JSON format. Optional

Context Output

There is no context output for this command.

Command example

!ctix-make-request type=POST endpoint=ingestion/notes/ body="{\"text\": \"this is the old text\",\"type\": \"threatdata\",\"meta_data\": {\"component\": \"threatdata\",\"object_id\": \"ba82b524-15b3-4071-8008-e58754f8d134\",\"type\": \"indicator\"},\"object_id\": \"ba82b524-15b3-4071-8008-e58754f8d134\"}"

Context Example

{
    "CTIX": {
        "Request": {
            "POST": {
                "ingestion/notes/": {
                    "created": 1674173772,
                    "created_by": {
                        "email": "some.user@example.com",
                        "first_name": "some",
                        "id": "5b03c17e-a1f8-43ab-b0d5-9e178fb95c4a",
                        "last_name": "user"
                    },
                    "id": "f8f67182-bf72-47df-9a90-31b2bd829a9d",
                    "is_json": false,
                    "meta_data": {
                        "component": "threatdata",
                        "object_id": "ba82b524-15b3-4071-8008-e58754f8d134",
                        "type": "indicator"
                    },
                    "modified": 1674173772,
                    "modified_by": {
                        "email": "some.user@example.com",
                        "first_name": "some",
                        "id": "5b03c17e-a1f8-43ab-b0d5-9e178fb95c4a",
                        "last_name": "user"
                    },
                    "object_id": "ba82b524-15b3-4071-8008-e58754f8d134",
                    "text": "this is the old text",
                    "title": null,
                    "type": "threatdata"
                }
            }
        }
    }
}

Human Readable Output

HTTP Response Data

created created_by id is_json meta_data modified modified_by object_id text type
1674173772 email: some.user@example.com, first_name: some, id: 5b03c17e-a1f8-43ab-b0d5-9e178fb95c4a, last_name: user f8f67182-bf72-47df-9a90-31b2bd829a9d false component: threatdata, object_id: ba82b524-15b3-4071-8008-e58754f8d134, type: indicator 1674173772 email: some.user@example.com, first_name: some, id: 5b03c17e-a1f8-43ab-b0d5-9e178fb95c4a, last_name: user ba82b524-15b3-4071-8008-e58754f8d134 this is the old text threatdata

Command example

!ctix-make-request type=GET endpoint=ingestion/notes/ params="{\"page\": 1, \"page_size\": 1}"

Context Example

{
    "CTIX": {
        "Request": {
            "GET": {
                "ingestion/notes/": {
                    "created": 1674173772,
                    "created_by": {
                        "email": "some.user@example.com",
                        "first_name": "some",
                        "id": "5b03c17e-a1f8-43ab-b0d5-9e178fb95c4a",
                        "last_name": "user"
                    },
                    "id": "f8f67182-bf72-47df-9a90-31b2bd829a9d",
                    "is_json": false,
                    "meta_data": {
                        "component": "threatdata",
                        "object_id": "ba82b524-15b3-4071-8008-e58754f8d134",
                        "type": "indicator"
                    },
                    "modified": 1674173772,
                    "modified_by": {
                        "email": "some.user@example.com",
                        "first_name": "some",
                        "id": "5b03c17e-a1f8-43ab-b0d5-9e178fb95c4a",
                        "last_name": "user"
                    },
                    "object_id": "ba82b524-15b3-4071-8008-e58754f8d134",
                    "text": "this is the old text",
                    "title": null,
                    "type": "threatdata"
                }
            }
        }
    }
}

Human Readable Output

HTTP Response Data

created created_by id is_json meta_data modified modified_by object_id text type
1674173772 email: some.user@example.com, first_name: some, id: 5b03c17e-a1f8-43ab-b0d5-9e178fb95c4a, last_name: user f8f67182-bf72-47df-9a90-31b2bd829a9d false component: threatdata, object_id: ba82b524-15b3-4071-8008-e58754f8d134, type: indicator 1674173772 email: some.user@example.com, first_name: some, id: 5b03c17e-a1f8-43ab-b0d5-9e178fb95c4a, last_name: user ba82b524-15b3-4071-8008-e58754f8d134 this is the old text threatdata

Command example

!ctix-make-request type=PUT endpoint=ingestion/notes/7d739870-ce7d-415b-bbbf-25f4bbc6be66/ body="{\"text\": \"this is the new text\"}"

Context Example

{
    "CTIX": {
        "Request": {
            "PUT": {
                "ingestion/notes/7d739870-ce7d-415b-bbbf-25f4bbc6be66/": {
                    "created": 1671821868,
                    "created_by": {
                        "email": "some.user@example.com",
                        "first_name": "some",
                        "id": "5b03c17e-a1f8-43ab-b0d5-9e178fb95c4a",
                        "last_name": "user"
                    },
                    "id": "7d739870-ce7d-415b-bbbf-25f4bbc6be66",
                    "is_json": false,
                    "meta_data": {
                        "component": "threatdata",
                        "object_id": "fake",
                        "type": "indicator"
                    },
                    "modified": 1674173787,
                    "modified_by": {
                        "email": "some.user@example.com",
                        "first_name": "some",
                        "id": "5b03c17e-a1f8-43ab-b0d5-9e178fb95c4a",
                        "last_name": "user"
                    },
                    "object_id": "fake",
                    "text": "this is the new text",
                    "title": null,
                    "type": "threatdata"
                }
            }
        }
    }
}

Human Readable Output

HTTP Response Data

created created_by id is_json meta_data modified modified_by object_id text type
1671821868 email: some.user@example.com, first_name: some, id: 5b03c17e-a1f8-43ab-b0d5-9e178fb95c4a, last_name: user 7d739870-ce7d-415b-bbbf-25f4bbc6be66 false component: threatdata, object_id: fake, type: indicator 1674173787 email: some.user@example.com, first_name: some, id: 5b03c17e-a1f8-43ab-b0d5-9e178fb95c4a, last_name: user fake this is the new text threatdata

Command example

!ctix-make-request type=DELETE endpoint=ingestion/notes/1e2f348b-8168-4330-933b-24263ab9116a/

Context Example

{
    "CTIX": {
        "Request": {
            "DELETE": {
                "ingestion/notes/1e2f348b-8168-4330-933b-24263ab9116a/": {
                    "details": "success"
                }
            }
        }
    }
}

Human Readable Output

HTTP Response Data

details
success

ctix-get-vulnerability-data


Lookup vulnerability data

Base Command

ctix-get-vulnerability-data

Input

Argument Name Description Required
cve The CVE identifier to look up information about Required
extra_fields A comma separated list of extra fields to return in the response Optional

Context Output

Path Type Description
CTIX.VulnerabilityLookup.cpes string CPEs
CTIX.VulnerabilityLookup.cvss2 number CVSS2
CTIX.VulnerabilityLookup.cvss3 number CVSS3
CTIX.VulnerabilityLookup.dbot_reputation integer DbotReputation
CTIX.VulnerabilityLookup.description string Description
CTIX.VulnerabilityLookup.last_modified string LastModified
CTIX.VulnerabilityLookup.created string LastPublished
CTIX.VulnerabilityLookup.name string Name
CTIX.VulnerabilityLookup.uuid string UUID
CTIX.VulnerabilityLookup.extra_data string Extra data

Command example

```!ctix-get-vulnerability-data cve=CVE-2023-30837``

Human Readable Output

HTTP Response Data

cpes cvss2 cvss3 dbot_reputation description extra_data last_modified last_published name uuid
cpe:2.3:a:vyper_project:vyper:::::::: None None 3 Remote exploitation of a design error vulnerability in Vyper_project Vyper could could allow an attacker to cause a Denial of Service (DoS) condition on the targeted host. A design error vulnerability has been identified in Vyper. Specifically, this issue occurs due to storage allocator overflow. Further details are not available at the time of this writing. ACTI will update this report as more details become available. {} 2023-05-08 05:48:58 2023-05-08 05:48:58 CVE-2023-30837 32316b0b-58a4-4f14-8d06-3e1678841eca

cve


Lookup vulnerability info

Base Command

cve

Input

Argument Name Description Required
cve The CVE identifier to look up information about Required
extra_fields A comma separated list of extra fields to return in the response Optional

Context Output

Path Type Description
CTIX.VulnerabilityLookup.cpes string CPEs
CTIX.VulnerabilityLookup.cvss2 number CVSS2
CTIX.VulnerabilityLookup.cvss3 number CVSS3
CTIX.VulnerabilityLookup.dbot_reputation integer DbotReputation
CTIX.VulnerabilityLookup.description string Description
CTIX.VulnerabilityLookup.last_modified string LastModified
CTIX.VulnerabilityLookup.created string LastPublished
CTIX.VulnerabilityLookup.name string Name
CTIX.VulnerabilityLookup.uuid string UUID
CTIX.VulnerabilityLookup.extra_data string Extra data
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.

Command example

```!cve cve=CVE-2023-30837``

Human Readable Output

HTTP Response Data

cpes cvss2 cvss3 dbot_reputation description extra_data last_modified last_published name uuid
cpe:2.3:a:vyper_project:vyper:::::::: None None 3 Remote exploitation of a design error vulnerability in Vyper_project Vyper could could allow an attacker to cause a Denial of Service (DoS) condition on the targeted host. A design error vulnerability has been identified in Vyper. Specifically, this issue occurs due to storage allocator overflow. Further details are not available at the time of this writing. ACTI will update this report as more details become available. {} 2023-05-08 05:48:58 2023-05-08 05:48:58 CVE-2023-30837 32316b0b-58a4-4f14-8d06-3e1678841eca

ctix-bulk-ioc-lookup-advanced


Performs a bulk lookup for threat data objects in Cyware Intel Exchange and retrieves details such as basic info, enriched data, and relations.

Base Command

ctix-bulk-ioc-lookup-advanced

Input

Argument Name Description Required
object_type The SDO object type to look up (e.g. indicator, malware, threat-actor). Required
values Comma-separated list of up to 100 threat data object values to look up. Optional
object_ids Comma-separated list of up to 100 threat data object IDs to look up. Optional
enrichment_data Pass true to retrieve the latest five enrichment data objects. Default is false. Possible values are: true, false. Optional
relation_data Pass true to retrieve the latest 100 relation details. Default is false. Possible values are: true, false. Optional
enrichment_tools Comma-separated list of up to five enrichment tool names to filter enrichment data. Optional
fields Comma-separated list of field names to retrieve specific details. By default all fields are retrieved. Optional
page Enter the Page number for pagination. Optional
page_size Enter the Page Size for pagination. Optional

Context Output

Path Type Description
CTIX.BulkIOCLookupAdvanced.id String ID of the threat data object.
CTIX.BulkIOCLookupAdvanced.name String Value of the threat data object.
CTIX.BulkIOCLookupAdvanced.object_type String SDO type of the threat data object.
CTIX.BulkIOCLookupAdvanced.ioc_type String IOC type (hash type for hashes, indicator type key for others).
CTIX.BulkIOCLookupAdvanced.sub_type String Sub-type of the indicator.
CTIX.BulkIOCLookupAdvanced.confidence_score Number Confidence score calculated by the Cyware Intel Exchange confidence score engine.
CTIX.BulkIOCLookupAdvanced.analyst_score String Score assigned to the threat data object by an analyst.
CTIX.BulkIOCLookupAdvanced.tlp String TLP assigned to the threat data object by the source.
CTIX.BulkIOCLookupAdvanced.analyst_tlp String TLP assigned to the threat data object by an analyst.
CTIX.BulkIOCLookupAdvanced.country String Country where the threat data object was seen.
CTIX.BulkIOCLookupAdvanced.description String Source description of the threat data.
CTIX.BulkIOCLookupAdvanced.is_deprecated Boolean True if the IOC is marked as deprecated.
CTIX.BulkIOCLookupAdvanced.is_false_positive Boolean True if the IOC is marked as a false positive.
CTIX.BulkIOCLookupAdvanced.is_reviewed Boolean True if the threat data object is manually reviewed.
CTIX.BulkIOCLookupAdvanced.is_whitelisted Boolean True if the IOC is marked as an allowed indicator.
CTIX.BulkIOCLookupAdvanced.manual_review Boolean True if the threat data is marked for manual review by an analyst.
CTIX.BulkIOCLookupAdvanced.created Number Source created timestamp of the threat data object.
CTIX.BulkIOCLookupAdvanced.modified Number Source modified timestamp of the threat data object.
CTIX.BulkIOCLookupAdvanced.ctix_created Number Created timestamp of the threat data object in Cyware Intel Exchange.
CTIX.BulkIOCLookupAdvanced.ctix_modified Number Last modified timestamp of the threat data object in Cyware Intel Exchange.
CTIX.BulkIOCLookupAdvanced.first_seen Number First seen timestamp of the threat data object.
CTIX.BulkIOCLookupAdvanced.last_seen Number Last seen timestamp of the threat data object.
CTIX.BulkIOCLookupAdvanced.valid_from Number Timestamp since when this threat data object is valid.
CTIX.BulkIOCLookupAdvanced.valid_until Number Timestamp until when this threat data object is valid.
CTIX.BulkIOCLookupAdvanced.sources Unknown List of sources that reported the threat data object.
CTIX.BulkIOCLookupAdvanced.tags Unknown Tags associated with the threat data object.
CTIX.BulkIOCLookupAdvanced.published_collections Unknown List of collections in which the IOC is published.
CTIX.BulkIOCLookupAdvanced.relations Unknown List of related threat data objects (requires relation_data=true).
CTIX.BulkIOCLookupAdvanced.enrichment_data Unknown List of enrichment objects from enrichment tools (requires enrichment_data=true).
CTIX.BulkIOCLookupAdvanced.custom_attributes Unknown List of custom attributes with name and value details.

Command Example

!ctix-bulk-ioc-lookup-advanced object_type=indicator value=1.2.3.4,evil.example.com enrichment_data=true relation_data=true

Context Example

{
    "CTIX": {
        "BulkIOCLookupAdvanced": {
            "confidence_score": 85,
            "country": null,
            "created": 1674080000,
            "ctix_created": 1674080000,
            "ctix_modified": 1674080001,
            "enrichment_data": [],
            "first_seen": null,
            "id": "6779a969-6404-4dd7-97ef-dec877c03c4f",
            "ioc_type": "ipv4-addr",
            "is_deprecated": false,
            "is_false_positive": false,
            "is_reviewed": false,
            "is_whitelisted": false,
            "last_seen": null,
            "modified": 1674080001,
            "name": "1.2.3.4",
            "object_type": "indicator",
            "relations": [],
            "sources": [
                {
                    "id": "38102b0e-1af4-4ee2-a62e-dd5f2ffaff5a",
                    "name": "Example Feed",
                    "source_type": "API_FEEDS"
                }
            ],
            "sub_type": "value",
            "tags": [],
            "tlp": "AMBER",
            "valid_from": 1674080000,
            "valid_until": null
        }
    }
}

Human Readable Output

Bulk IOC Lookup Advanced

confidence_score created ctix_created ctix_modified id ioc_type is_deprecated is_false_positive is_reviewed is_whitelisted modified name object_type sources sub_type tlp valid_from
85 1674080000 1674080000 1674080001 6779a969-6404-4dd7-97ef-dec877c03c4f ipv4-addr false false false false 1674080001 1.2.3.4 indicator {‘id’: ‘38102b0e-1af4-4ee2-a62e-dd5f2ffaff5a’, ‘name’: ‘Example Feed’, ‘source_type’: ‘API_FEEDS’} value AMBER 1674080000

Configuration parameters

  • base_url — Endpoint URL (required)
  • access_id — Access Key (required)
  • secret_key — Secret Key (required)
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings
  • timeout — Timeout
  • isFetch — Fetch incidents
  • incidentFetchInterval — Incidents Fetch Interval
  • incidentType — Incident type
  • classifier — Classifier
  • mapper — Mapper (incoming)
  • integrationReliability — Source Reliability
  • first_fetch — First fetch time
  • max_fetch — Maximum number of incidents per fetch
  • incident_fetch_query — Incident Fetch CQL Query
  • feed — Fetch indicators
  • saved_result_set_label — Saved Result Set Label
  • saved_result_set_version — Saved Result Set Version
  • retrieve_enriched_data — Retrieve Enriched Data
  • feedFetchInterval — Feed Fetch Interval
  • feedReliability — Source Reliability (required)
  • feedReputation — Indicator Reputation
  • feedExpirationPolicy
  • feedExpirationInterval
  • tlp_color — Traffic Light Protocol Color
  • feedBypassExclusionList — Bypass exclusion list
  • feedTags — Tags

Commands (39)

  • ctix-add-analyst-score

    Add Analyst Score for a Threat data.

  • ctix-add-analyst-tlp

    Add Analyst TLP.

  • ctix-add-indicator-as-false-positive

    Add indicators as false positive in CTIX.

  • ctix-add-tag-indicator

    Adding Tag to Indicator.

  • ctix-allowed-iocs

    Adds list of same type of iocs to allowed.

  • ctix-bulk-ioc-lookup-advanced

    Performs a bulk lookup for threat data objects in the CTIX platform and retrieves details such as basic info, enriched data, and relations.

  • ctix-create-note

    Creates a new Note from the parameter 'text'.

  • ctix-create-tag

    Create new tag in the ctix platform.

  • ctix-delete-note

    Deletes an existing Note, as specified by its ID.

  • ctix-deprecate-ioc

    Deprecate ioc bulk api.

  • ctix-disable-or-enable-tags

    Disables or enables one or more tags based on their IDs.

  • ctix-get-actions

    Enrichment tools listing API.

  • ctix-get-all-notes

    Get paginated list of Notes.

  • ctix-get-allowed-iocs

    get paginated list of allowed iocs.

  • ctix-get-conversion-feed-source

    Get Conversion feed source.

  • ctix-get-create-threat-data

    Gets or creates threat data.

  • ctix-get-indicator-details

    Get Indicator Details.

  • ctix-get-indicator-observations

    Get Indicator Observations.

  • ctix-get-indicator-tags

    Get Indicator Tags.

  • ctix-get-lookup-threat-data

    Lookup to get threat data.

  • ctix-get-note-details

    Get details of a Note as specified by its ID.

  • ctix-get-object-relations

    Get Object Relations.

  • ctix-get-saved-searches

    Saved Search listing api with pagination.

  • ctix-get-server-collections

    Source Collection listing api with pagination.

  • ctix-get-tags

    Get paginated list of tags.

  • ctix-get-threat-data

    Command for querying and listing threat data.

  • ctix-get-vulnerability-data

    Lookup vulnerability info.

  • ctix-ioc-manual-review

    Adds ioc to manual review bulk api.

  • ctix-make-request

    allows you to make any HTTP request using CTIX endpoints.

  • ctix-remove-allowed-ioc

    Removes a allowed ioc with given id.

  • ctix-remove-tag-from-indicator

    Remove Tag From Indicator.

  • ctix-saved-result-set

    Saved Result Set.

  • ctix-search-for-tag

    Search for tag.

  • ctix-update-note

    Updates the Note text from an existing Note, as specified by its ID.

  • cve

    Lookup vulnerability info.

  • domain

    Lookup domain threat data.

  • file

    Lookup file threat data.

  • ip

    Lookup ip threat data.

  • url

    Lookup url threat data.

import base64
import hashlib
import hmac
import json
import time
import urllib.parse
import uuid
from collections.abc import Callable
from datetime import datetime, UTC
from http import HTTPStatus
from typing import Any, cast

import dateparser
import demistomock as demisto
import requests
import urllib3
from CommonServerPython import *

from CommonServerUserPython import *


def is_valid_uuid(uuid_string):
    try:
        uuid.UUID(uuid_string)
        return True
    except ValueError:
        return False


# register_module_line("CTIX v3", "start", __line__())
# Uncomment while development=


"""IMPORTS"""


# Disable insecure warnings
urllib3.disable_warnings()

"""GLOBALS"""

domain_regex = (
    "([a-z¡-\uffff0-9](?:[a-z¡-\uffff0-9-]{0,61}"
    "[a-z¡-\uffff0-9])?(?:\\.(?!-)[a-z¡-\uffff0-9-]{1,63}(?<!-))*"
    "\\.(?!-)(?!(jpg|jpeg|exif|tiff|tif|png|gif|otf|ttf|fnt|dtd|xhtml|css"
    "|html)$)(?:[a-z¡-\uffff-]{2,63}|xn--[a-z0-9]{1,59})(?<!-)\\.?$"
    "|localhost)"
)

tag_colors = {
    "blue": "#0068FA",
    "purple": "#5236E2",
    "orange": "#EB9C00",
    "red": "#FF5330",
    "green": "#27865F",
    "yellow": "#C4C81D",
    "turquoise": "#00A2C2",
    "pink": "#C341E7",
    "light-red": "#AD6B76",
    "grey": "#95A1B1",
}

CTIX_DBOT_MAP = {
    "ipv4-addr": "ip",
    "ipv6-addr": "ip",
    "MD5": "file",
    "SHA-1": "file",
    "SHA-224": "file",
    "SHA-256": "file",
    "SHA-384": "file",
    "SHA-512": "file",
    "SSDEEP": "file",
    "domain-name": "domain",
    "domain": "domain",
    "email-addr": "email",
    "email-message": "email",
    "artifact": "custom",
    "network-traffic": "custom",
    "user-agent": "custom",
    "windows-registry-key": "custom",
    "directory": "custom",
    "process": "custom",
    "software": "custom",
    "user-account": "custom",
    "mac-addr": "custom",
    "mutex": "custom",
    "autonomous-system": "custom",
    "cidr": "custom",
    "certificate": "x509-certificate",
    "url": "url",
}

REGEX_MAP = {
    "url": re.compile(urlRegex, regexFlags),
    "domain": re.compile(domain_regex, regexFlags),
    "hash": re.compile(hashRegex, regexFlags),
}

"""
Canonical map for indicator type -> XSOAR type
"""
INDICATOR_TYPE_MAP: dict[str, str] = {
    "ipv4-addr": FeedIndicatorType.IP,
    "ipv6-addr": FeedIndicatorType.IPv6,
    "domain-name": FeedIndicatorType.Domain,
    "domain": FeedIndicatorType.Domain,
    "url": FeedIndicatorType.URL,
    "email-addr": FeedIndicatorType.Email,
    "email-message": FeedIndicatorType.Email,
    "MD5": FeedIndicatorType.File,
    "SHA-1": FeedIndicatorType.File,
    "SHA-224": FeedIndicatorType.File,
    "SHA-256": FeedIndicatorType.File,
    "SHA-384": FeedIndicatorType.File,
    "SHA-512": FeedIndicatorType.File,
    "SSDEEP": FeedIndicatorType.SSDeep,
    "autonomous-system": FeedIndicatorType.AS,
    "cidr": FeedIndicatorType.CIDR,
    "certificate": FeedIndicatorType.X509,
    "user-agent": "User Agent",
    "windows-registry-key": FeedIndicatorType.Registry,
    "mutex": FeedIndicatorType.MUTEX,
    "mac-addr": "MAC Address",
    "network-traffic": "Network Traffic",
    "artifact": "Artifact",
    "directory": "Directory",
    "process": "Process",
    "software": FeedIndicatorType.Software,
    "user-account": FeedIndicatorType.Account,
}
CYWARE_TYPE_NORMALIZATION: dict[str, str] = {
    "ip": INDICATOR_TYPE_MAP.get("ipv4-addr", FeedIndicatorType.IP),
    "ipv4": INDICATOR_TYPE_MAP.get("ipv4-addr", FeedIndicatorType.IP),
    "ipv4-addr": INDICATOR_TYPE_MAP.get("ipv4-addr", FeedIndicatorType.IP),
    "ipv6": INDICATOR_TYPE_MAP.get("ipv6-addr", FeedIndicatorType.IPv6),
    "ipv6-addr": INDICATOR_TYPE_MAP.get("ipv6-addr", FeedIndicatorType.IPv6),
    "domain": INDICATOR_TYPE_MAP.get("domain", FeedIndicatorType.Domain),
    "domain-name": INDICATOR_TYPE_MAP.get("domain", FeedIndicatorType.Domain),
    "url": INDICATOR_TYPE_MAP.get("url", FeedIndicatorType.URL),
    "email": INDICATOR_TYPE_MAP.get("email-addr", FeedIndicatorType.Email),
    "email-addr": INDICATOR_TYPE_MAP.get("email-addr", FeedIndicatorType.Email),
    "hash_md5": INDICATOR_TYPE_MAP.get("MD5", FeedIndicatorType.File),
    "hash_sha1": INDICATOR_TYPE_MAP.get("SHA-1", FeedIndicatorType.File),
    "hash_sha256": INDICATOR_TYPE_MAP.get("SHA-256", FeedIndicatorType.File),
    "md5": INDICATOR_TYPE_MAP.get("MD5", FeedIndicatorType.File),
    "sha-1": INDICATOR_TYPE_MAP.get("SHA-1", FeedIndicatorType.File),
    "sha-256": INDICATOR_TYPE_MAP.get("SHA-256", FeedIndicatorType.File),
    "sha1": INDICATOR_TYPE_MAP.get("SHA-1", FeedIndicatorType.File),
    "sha256": INDICATOR_TYPE_MAP.get("SHA-256", FeedIndicatorType.File),
    "ssdeep": INDICATOR_TYPE_MAP.get("SSDEEP", FeedIndicatorType.SSDeep),
    "file": FeedIndicatorType.File,
}

# Maps raw Cyware/CTIX IOC type strings → the XSOAR hash field name to populate
_HASH_TYPE_TO_FIELD: dict[str, str] = {
    # MD5
    "hash_md5": "md5",
    "md5": "md5",
    "MD5": "md5",
    # SHA-1
    "hash_sha1": "sha1",
    "sha1": "sha1",
    "sha-1": "sha1",
    "SHA-1": "sha1",
    # SHA-256
    "hash_sha256": "sha256",
    "sha256": "sha256",
    "sha-256": "sha256",
    "SHA-256": "sha256",
    # SHA-224 / SHA-384 / SHA-512
    "SHA-224": "sha224",
    "sha-224": "sha224",
    "SHA-384": "sha384",
    "sha-384": "sha384",
    "SHA-512": "sha512",
    "sha-512": "sha512",
    # SSDEEP
    "SSDEEP": "ssdeep",
    "ssdeep": "ssdeep",
}

_STIX_SDO_TO_XSOAR_ENTITY_TYPE: dict[str, str] = {
    # ── Standard indicator / observable types ──────────────────────────────────
    "ipv4-addr": FeedIndicatorType.IP,
    "ipv6-addr": FeedIndicatorType.IPv6,
    "domain-name": FeedIndicatorType.Domain,
    "domain": FeedIndicatorType.Domain,
    "url": FeedIndicatorType.URL,
    "email-addr": FeedIndicatorType.Email,
    "email-message": FeedIndicatorType.Email,
    "file": FeedIndicatorType.File,
    "MD5": FeedIndicatorType.File,
    "SHA-1": FeedIndicatorType.File,
    "SHA-224": FeedIndicatorType.File,
    "SHA-256": FeedIndicatorType.File,
    "SHA-384": FeedIndicatorType.File,
    "SHA-512": FeedIndicatorType.File,
    "SSDEEP": FeedIndicatorType.SSDeep,
    "autonomous-system": FeedIndicatorType.AS,
    "cidr": FeedIndicatorType.CIDR,
    "windows-registry-key": FeedIndicatorType.Registry,
    "mutex": FeedIndicatorType.MUTEX,
    "certificate": FeedIndicatorType.X509,
    "x509-certificate": FeedIndicatorType.X509,
    "software": FeedIndicatorType.Software,
    "user-account": FeedIndicatorType.Account,
    "user-agent": "User Agent",
    "mac-addr": "MAC Address",
    "network-traffic": "Network Traffic",
    "malware": ThreatIntel.ObjectsNames.MALWARE,
    "threat-actor": ThreatIntel.ObjectsNames.THREAT_ACTOR,
    "attack-pattern": ThreatIntel.ObjectsNames.ATTACK_PATTERN,
    "campaign": ThreatIntel.ObjectsNames.CAMPAIGN,
    "course-of-action": ThreatIntel.ObjectsNames.COURSE_OF_ACTION,
    "intrusion-set": ThreatIntel.ObjectsNames.INTRUSION_SET,
    "tool": ThreatIntel.ObjectsNames.TOOL,
    "report": ThreatIntel.ObjectsNames.REPORT,
    "infrastructure": ThreatIntel.ObjectsNames.INFRASTRUCTURE,
    "vulnerability": FeedIndicatorType.CVE,
    "indicator": "Indicator",
    "observable": "Indicator",
    "identity": "Identity",
    "location": "Location",
}

ENRICHMENT_BATCH_SIZE = 100
BULK_LOOKUP_PAGE_SIZE = 100
# Max ``next`` pages to follow per bulk IOC lookup batch (safety cap)
BULK_LOOKUP_MAX_PAGES = 100
FETCH_DATE_FORMAT = "%Y-%m-%dT%H:%M:%SZ"
DELTA_TIME_DIFF = 2
FIRST_FETCH_DEFAULT_MINUTES = 4320

# API page-size limits (per CTIX API documentation)
PAGE_SIZE = 100
PAGE_SIZE_THREAT_DATA = 2000

# LastRun state keys (instance namespaced)
FETCH_INCIDENTS_STATE_PREFIX = "fetch_incidents"
FETCH_INDICATORS_STATE_PREFIX = "fetch_indicators"

RATE_LIMIT_STATUS_ERR = "status-> 429"
RETRY_COUNT = 0
""" CLIENT CLASS """


class Client(BaseClient):
    """
    Client to use in the CTIX integration. Overrides BaseClient
    """

    def __init__(
        self,
        base_url: str,
        access_id: str,
        secret_key: str,
        timeout: int,
        verify: bool,
        proxies: dict,
    ) -> None:
        self.base_url = base_url
        self.access_id = access_id
        self.secret_key = secret_key
        self.timeout = timeout
        self.verify = verify
        self.proxies = proxies

    def signature(self, expires: int):
        """
        Signature Generation

        :param int expires: Epoch time in which time when signature will expire
        :return str signature : signature queryset
        """
        to_sign = f"{self.access_id}\n{expires}"
        return base64.b64encode(hmac.new(self.secret_key.encode("utf-8"), to_sign.encode("utf-8"), hashlib.sha1).digest()).decode(
            "utf-8"
        )

    def add_common_params(self, params: dict):
        """
        Add Common Params

        :param dict params: Paramters to be added in request
        :return dict: Params dictionary with AccessID, Expires and Signature
        """
        expires = int(time.time() + 15)
        params["AccessID"] = self.access_id
        params["Expires"] = expires
        params["Signature"] = self.signature(expires)
        return params

    def get_http_request(self, full_url: str, payload: dict = None, fallback_full_url: str = None, params: dict = None, **kwargs):
        """
        GET HTTP Request

        :param str full_url: URL to be called
        :param dict payload: Request body, defaults to None
        :param str fallback_full_url: URL to be called if the first 404s, defaults to None
        :raises DemistoException: If Any error is found will be raised on XSOAR
        :return dict: Response object
        """
        kwargs = self.add_common_params(kwargs)
        params and kwargs.update(params)
        separator = "&" if "?" in full_url else "?"
        full_url = full_url + separator + urllib.parse.urlencode(kwargs)

        headers = {"content-type": "application/json"}
        resp = requests.get(
            full_url,
            verify=self.verify,
            proxies=self.proxies,
            timeout=self.timeout,
            headers=headers,
            json=payload,
        )
        status_code = resp.status_code
        try:
            resp.raise_for_status()  # Raising an exception for non-200 status code
            response = {"data": resp.json(), "status": status_code}
            return response
        except requests.exceptions.HTTPError:
            if status_code == HTTPStatus.NOT_FOUND:
                if fallback_full_url:
                    # try again with the fallback url
                    return self.get_http_request(fallback_full_url, payload, **kwargs)
                else:
                    return_error("Your CTIX version does not support this command.")
            else:
                raise DemistoException(f"Error: status-> {status_code!r}; Reason-> {resp.reason!r}]")

    def post_http_request(self, full_url: str, payload: dict, params: dict, fallback_full_url: str = None):
        """
        POST HTTP Request

        :param str full_url: URL to be called
        :param dict payload: Request body, defaults to None
        :param str fallback_full_url: URL to be called if the first 404s, defaults to None
        :raises DemistoException: If Any error is found will be raised on XSOAR
        :return dict: Response object
        """
        headers = {"content-type": "application/json"}
        params = self.add_common_params(params)
        separator = "&" if "?" in full_url else "?"
        full_url = full_url + separator + urllib.parse.urlencode(params)
        resp = requests.post(
            full_url,
            verify=self.verify,
            proxies=self.proxies,
            json=payload,
            headers=headers,
            timeout=self.timeout,
        )
        status_code = resp.status_code
        try:
            resp.raise_for_status()  # Raising an exception for non-200 status code
            response = {"data": resp.json(), "status": status_code}
            return response
        except requests.exceptions.HTTPError:
            if status_code == HTTPStatus.NOT_FOUND:
                if fallback_full_url:
                    # try again with the fallback url
                    return self.post_http_request(fallback_full_url, payload, params)
                else:
                    return_error("Your CTIX version does not support this command.")
            else:
                raise DemistoException(f"Error: status-> {status_code!r}; Reason-> {resp.reason!r}]")

    def put_http_request(self, full_url: str, payload: dict, params: dict):
        """
        PUT HTTP Request

        :param str full_url: URL to be called
        :param dict payload: Request body, defaults to None
        :raises DemistoException: If Any error is found will be raised on XSOAR
        :return dict: Response object
        """
        headers = {"content-type": "application/json"}
        params = self.add_common_params(params)
        separator = "&" if "?" in full_url else "?"
        full_url = full_url + separator + urllib.parse.urlencode(params)
        resp = requests.put(
            full_url,
            verify=self.verify,
            proxies=self.proxies,
            json=payload,
            headers=headers,
            timeout=self.timeout,
        )
        status_code = resp.status_code
        try:
            resp.raise_for_status()  # Raising an exception for non-200 status codeg
            response = {"data": resp.json(), "status": status_code}
            return response
        except requests.exceptions.HTTPError:
            if status_code == HTTPStatus.NOT_FOUND:
                return_error("Your CTIX version does not support this command.")
            else:
                raise DemistoException(f"Error: status-> {status_code!r}; Reason-> {resp.reason!r}]")

    def delete_http_request(self, full_url: str, payload: dict = None, params: dict = None, **kwargs):
        """
        DELETE HTTP Request

        :param str full_url: URL to be called
        :param dict payload: Request body, defaults to None
        :raises DemistoException: If Any error is found will be raised on XSOAR
        :return dict: Response object
        """
        kwargs = self.add_common_params(kwargs)
        params and kwargs.update(params)
        separator = "&" if "?" in full_url else "?"
        full_url = full_url + separator + urllib.parse.urlencode(kwargs)
        headers = {"content-type": "application/json"}
        resp = requests.delete(
            full_url,
            verify=self.verify,
            proxies=self.proxies,
            timeout=self.timeout,
            headers=headers,
            json=payload,
        )
        status_code = resp.status_code
        try:
            resp.raise_for_status()  # Raising an exception for non-200 status code
            response = {"data": resp.json(), "status": status_code}
            return response
        except requests.exceptions.HTTPError:
            if status_code == HTTPStatus.BAD_REQUEST:
                response = {"data": {}, "status": status_code}
                return response

            raise DemistoException(f"Error: status-> {status_code!r}; Reason-> {resp.reason!r}]")

    def follow_next_page(
        self,
        next_url: str,
        payload: dict | None = None,
        method: str = "GET",
    ) -> dict | None:
        """Follow a pagination ``next`` URL returned by the CTIX API.

        The ``next`` value is a relative path with embedded query params,
        e.g. ``ingestion/rules/save_result_set/?page=2&page_size=100&...``.
        Auth params (AccessID, Expires, Signature) are appended automatically
        by the underlying HTTP helpers.

        :param str next_url: Relative URL path (must not be empty)
        :param dict payload: Request body – required for POST-based endpoints
        :param str method: ``"GET"`` (default) or ``"POST"``
        :return: Standard ``{"data": ..., "status": ...}`` response dict
        """
        if not next_url:
            return None
        full_url = self.base_url + next_url
        if method.upper() == "POST":
            return self.post_http_request(full_url, payload or {}, {})
        return self.get_http_request(full_url, payload)

    def test_auth(self):
        """
        Test authentication

        :return dict: Returns result for ping
        """
        client_url = self.base_url + "ping/"
        return self.get_http_request(client_url)

    def create_tag(self, name: str, color_code: str):
        """Creates a tag in ctix platform
        :type name: ``str``
        :param name: Name of the tag

        :type color_code: ``str``
        :param color_code: Hex color code of the tag e.g #111111

        :return: dict containing the details of newly created tag
        :rtype: ``Dict[str, Any]``
        """
        url_suffix = "ingestion/tags/"
        client_url = self.base_url + url_suffix
        payload = {"name": name, "color_code": color_code}
        return self.post_http_request(full_url=client_url, payload=payload, params={})

    def get_tags(self, page: int, page_size: int, q: str):
        """Paginated list of tags from ctix platform using page_number and page_size
        :type page: int
        :param page: page number for the pagination for list api

        :type page_size: int
        :param page_size: page size for the pagination for list api

        :type q: str
        :param q: search query string for the list api
        """
        url_suffix = "ingestion/tags/"
        client_url = self.base_url + url_suffix
        params = {"page": page, "page_size": page_size}
        if q:
            params["q"] = q  # type: ignore
        return self.get_http_request(client_url, params)

    def disable_or_enable_tag(self, tag_ids: list, action: str):
        """Enables or Disables a tag from the ctix instance
        :type tag_ids: ``list``
        :param tag_ids: id of the tag to be disabled or enabled
        :type action: ``str``
        :param action: Action to be performed. Possible values are 'enabled' and 'disabled'
        """
        url_suffix = "ingestion/tags/bulk-actions/"
        client_url = self.base_url + url_suffix
        return self.post_http_request(client_url, {"ids": tag_ids, "action": action}, {"component": "tags"})

    def whitelist_iocs(self, ioc_type, values, reason):
        url_suffix = "conversion/allowed_indicators/"  # for CTIX >= 3.6
        fallback_url_suffix = "conversion/whitelist/"  # for CTIX < 3.6
        client_url = self.base_url + url_suffix
        fallback_client_url = self.base_url + fallback_url_suffix
        payload = {"type": ioc_type, "values": values, "reason": reason}
        return self.post_http_request(client_url, payload, {}, fallback_full_url=fallback_client_url)

    def get_whitelist_iocs(self, page: int, page_size: int, q: str):
        """Paginated list of tags from ctix platform using page_number and page_size
        :type page: int
        :param page: page number for the pagination for list api

        :type page_size: int
        :param page_size: page size for the pagination for list api

        :type q: str
        :param q: search query string for the list api
        """
        url_suffix = "conversion/allowed_indicators/"  # for CTIX >= 3.6
        fallback_url_suffix = "conversion/whitelist/"  # for CTIX < 3.6
        client_url = self.base_url + url_suffix
        fallback_client_url = self.base_url + fallback_url_suffix
        params = {"page": page, "page_size": page_size}
        if q:
            params["q"] = q  # type: ignore
        return self.get_http_request(client_url, {}, fallback_full_url=fallback_client_url, params=params)

    def remove_whitelisted_ioc(self, whitelist_id: str):
        """Removes whitelisted ioc with given `whitelist_id`
        :type whitelist_id: str
        :param whitelist_id: id of the whitelisted ioc to be removed
        """
        url_suffix = "conversion/allowed_indicators/bulk-actions/"
        payload = {
            "ids": whitelist_id,
            "action": "delete",
        }
        client_url = self.base_url + url_suffix
        return self.post_http_request(full_url=client_url, payload=payload, params={})

    def get_threat_data(self, page: int, page_size: int, query: str):
        """
        Get Threat Data

        :param int page: Paginated number from where data will be polled
        :param int page_size: Size of the result
        :param str query: CQL query for polling specific result
        :return dict: Returns response for query
        """
        url_suffix = "ingestion/threat-data/list/"
        client_url = self.base_url + url_suffix
        params = {"page": page, "page_size": page_size}
        payload = {"query": query}
        return self.post_http_request(client_url, payload=payload, params=params)

    def get_saved_searches(self, page: int, page_size: int):
        """
        Get Saved Searches

        :param int page: Paginated number from where data will be polled
        :param int page_size: Size of the result
        :return dict: Returns response for query
        """
        url_suffix = "ingestion/saved-searches/"
        client_url = self.base_url + url_suffix
        params = {"page": page, "page_size": page_size}
        return self.get_http_request(client_url, {}, None, params=params)

    def get_server_collections(self, page: int, page_size: int):
        """
        Get Server Collections

        :param int page: Paginated number from where data will be polled
        :param int page_size: Size of the result
        :return dict: Returns response for query
        """
        url_suffix = "publishing/collection/"
        client_url = self.base_url + url_suffix
        params = {"page": page, "page_size": page_size}
        return self.get_http_request(client_url, {}, params=params)

    def get_actions(self, page: int, page_size: int, params: dict[str, Any]):
        """
        Get Actions

        :param int page: Paginated number from where data will be polled
        :param int page_size: Size of the result
        :param Dict[str, Any] params: Params to be send with request
        :return dict: Returns response for query
        """
        url_suffix = "ingestion/actions/"
        client_url = self.base_url + url_suffix
        params["page"] = page
        params["page_size"] = page_size
        return self.get_http_request(client_url, params=params)

    def add_indicator_as_false_positive(self, object_ids: List[str], object_type: str):
        """
        Add Indicator as False Positive

        :param list[str] object_ids: Object IDs of the IOCs
        :param str object_type: Object type of the IOCs
        :return dict: Returns response for query
        """
        url_suffix = "ingestion/threat-data/bulk-action/false_positive/"
        client_url = self.base_url + url_suffix
        payload = {"object_ids": object_ids, "object_type": object_type, "data": {}}

        return self.post_http_request(client_url, payload, {})

    def add_ioc_to_manual_review(self, object_ids: List[str], object_type: str):
        """
        Add IOC to Manual Review

        :param list[str] object_ids: Object IDs of the IOCs
        :param str object_type: Object type of the IOCs
        :return dict: Returns response for query
        """
        url_suffix = "ingestion/threat-data/bulk-action/manual_review/"
        client_url = self.base_url + url_suffix
        payload = {"object_ids": object_ids, "object_type": object_type, "data": {}}

        return self.post_http_request(client_url, payload, {})

    def deprecate_ioc(self, object_ids: str, object_type: str):
        """
        Deprecate IOC

        :param str object_ids: Object ID of the IOC
        :param str object_type: Object type of the IOC
        :return dict: Returns response for query
        """
        url_suffix = "ingestion/threat-data/bulk-action/deprecate/"
        client_url = self.base_url + url_suffix
        payload = {"object_ids": object_ids, "object_type": object_type, "data": {}}

        return self.post_http_request(client_url, payload, {})

    def add_analyst_tlp(self, object_id: str, object_type: str, data):
        """
        Add Analyst TLP

        :param str object_id: Object ID of the IOCs
        :param str object_type: _Object type of the IOCs
        :param dict data: data to be send over POST request
        :return dict: Returns response for query
        """
        url_suffix = "ingestion/threat-data/action/analyst_tlp/"
        client_url = self.base_url + url_suffix
        payload = {"object_id": object_id, "object_type": object_type, "data": data}

        return self.post_http_request(client_url, payload, {})

    def add_analyst_score(self, object_id: str, object_type, data):
        """
        Add Analyst Score

        :param str object_id: Object ID of the IOCs
        :param str object_type: Object type of the IOCs
        :param dict data: Request body to be send over POST request
        :return dict: Returns response for query
        """
        url_suffix = "ingestion/threat-data/action/analyst_score/"
        client_url = self.base_url + url_suffix
        payload = {"object_id": object_id, "object_type": object_type, "data": data}

        return self.post_http_request(client_url, payload, {})

    def saved_result_set(
        self,
        page: int,
        page_size: int,
        label_name: str = None,
        version: str = None,
        from_timestamp: int | None = None,
        to_timestamp: int | None = None,
    ):
        """
        Saved Result Set

        :param int page: Paginated number from where data will be polled
        :param int page_size: Size of the result
        :param str label_name: Label name used to get the data from the rule
        :param str version: Saved Result Set version
        :param int from_timestamp: Filter results from this epoch timestamp
        :param int to_timestamp: Filter results until this epoch timestamp
        :return dict: Returns response for query
        """
        url_suffix = "ingestion/rules/save_result_set/"
        client_url = self.base_url + url_suffix
        params: dict[str, Any] = {"page": page, "page_size": page_size}
        if version:
            params["version"] = version
        if label_name:
            params["label_name"] = label_name
        if from_timestamp:
            params["from_timestamp"] = from_timestamp
        if to_timestamp:
            params["to_timestamp"] = to_timestamp
        return self.get_http_request(client_url, {}, params=params)

    def tag_indicator_updation(
        self,
        q: str,
        page: int,
        page_size: int,
        object_id: str,
        object_type: str,
        tag_id: str,
        operation: str,
    ):
        """
        Tag Indicator Updation

        :param str q: query to be send
        :param int page: Paginated number from where data will be polled
        :param int page_size: Size of the result
        :param str object_id: Object ID of the IOCs
        :param str object_type: Object type of the IOCs
        :param str tag_id: Tag ID that will be removed or added
        :param str operation: Addition or Removal of tag operation
        :return dict: Returns response for query
        """
        tags_data = self.get_indicator_tags(object_type, object_id, {"page": page, "page_size": page_size})["data"]
        tags = [_["id"] for _ in tags_data["tags"]]
        data = {}
        url_suffix = ""
        if operation == "add_tag_indicator":
            url_suffix = "ingestion/threat-data/bulk-action/add_tag/"
            tags.extend([_.strip() for _ in tag_id.split(",")])
            data = {"tag_id": list(set(tags))}
        elif operation == "remove_tag_from_indicator":
            url_suffix = "ingestion/threat-data/bulk-action/remove_tag/"
            tags = [_.strip() for _ in tag_id.split(",")]
            data = {"tag_id": list(set(tags))}
        client_url = self.base_url + url_suffix
        params = {"page": page, "page_size": page_size, "q": q}
        payload = {
            "object_ids": [object_id],
            "object_type": object_type,
            "data": data,
        }
        return self.post_http_request(client_url, payload, params)

    def search_for_tag(self, params: dict):
        """
        Search for tag

        :param dict params: Paramters to be added in request
        :return dict: Returns response for query
        """
        url_suffix = "ingestion/tags/"
        client_url = self.base_url + url_suffix
        return self.get_http_request(client_url, **params)

    def get_indicator_details(self, object_type: str, object_id: str, params: dict):
        """
        Get Indicator Details

        :param str object_type: Object type of the IOCs
        :param str object_id: Object ID of the IOCs
        :param dict params: Paramters to be added in request
        :return dict: Returns response for query
        """
        url_suffix = f"ingestion/threat-data/{object_type}/{object_id}/basic/"
        client_url = self.base_url + url_suffix
        return self.get_http_request(client_url, **params)

    def get_indicator_tags(self, object_type: str, object_id: str, params: dict):
        """
        Get Indicator Tags

        :param str object_type: Object type of the IOCs
        :param str object_id: Object ID of the IOCs
        :param dict params: Paramters to be added in request
        :return dict: Returns response for query
        """
        url_suffix = f"ingestion/threat-data/{object_type}/{object_id}/quick-actions/"
        client_url = self.base_url + url_suffix
        return self.get_http_request(client_url, **params)

    def get_indicator_relations(self, object_type: str, object_id: str, params: dict):
        """
        Get Indicator Relations

        :param str object_type: Object type of the IOCs
        :param str object_id: Object ID of the IOCs
        :param dict params: Paramters to be added in request
        :return dict: Returns response for query
        """
        url_suffix = f"ingestion/threat-data/{object_type}/{object_id}/relations/"
        client_url = self.base_url + url_suffix
        return self.get_http_request(client_url, **params)

    def get_indicator_observations(self, params: dict):
        """
        Get Indicator Observations

        :param dict params: Paramters to be added in request
        :return dict: Returns response for query
        """
        url_suffix = "ingestion/threat-data/source-references/"
        client_url = self.base_url + url_suffix
        return self.get_http_request(client_url, **params)

    def get_conversion_feed_source(self, params: dict):
        """
        Get Conversion Feed Source

        :param dict params: Paramters to be added in request
        :return dict: Returns response for query
        """
        url_suffix = "conversion/feed-sources/"
        client_url = self.base_url + url_suffix
        return self.get_http_request(client_url, **params)

    def get_lookup_threat_data(self, object_type: str, ioc_type: list, object_names: list, params: dict):
        """
        Get Lookup Threat Data

        :param str object_type: (SDO) Object type of the IOCs
        :param list ioc_type: the IOC type of the Indicator (eg. URL, MD5, IPv4)
        :param list object_names: Indicator/IOCs names
        :param dict params: Paramters to be added in request
        :return dict: Returns response for query
        """
        url_suffix = "ingestion/threat-data/list/"
        query = f"type={object_type}"

        if len(ioc_type) == 1:
            query += f" AND ioc_type IN ('{ioc_type[0]}')"
        elif len(ioc_type) > 1:
            query += f" AND ioc_type IN {tuple(ioc_type)}"

        if len(object_names) == 1:
            query += f" AND value IN ('{object_names[0]}')"
        else:
            query += f" AND value IN {tuple(object_names)}"

        payload = {"query": query}
        client_url = self.base_url + url_suffix
        return self.post_http_request(client_url, payload, params)

    def bulk_lookup_and_create_data(self, object_names, source, collection, page_size):
        url_suffix = "ingestion/threat-data/bulk-lookup-and-create/"
        client_url = self.base_url + url_suffix
        params = {"create": "true", "page_size": page_size}

        payload = {
            "ioc_values": object_names,
            "metadata": {"tlp": "AMBER", "confidence": 100, "tags": []},
            "source": {"source_name": source},
            "collection": {"collection_name": collection},
        }

        return self.post_http_request(client_url, payload, params)

    def bulk_ioc_lookup_advanced(
        self,
        object_type: str,
        values: list,
        object_ids: list,
        enrichment_data: bool,
        relation_data: bool,
        enrichment_tools: str | None = None,
        fields: str | None = None,
        page: int | None = None,
        page_size: int | None = None,
    ):
        """
        Bulk IOC Lookup (Advanced)

        :param str object_type: SDO object type to lookup (path param)
        :param list values: list of indicator values to lookup
        :param list object_ids: list of object ids to lookup
        :param bool enrichment_data: pass True to include the latest enrichment data objects
        :param bool relation_data: pass True to include the latest relation details
        :param str enrichment_tools: optional comma-separated enrichment tool names
        :param str fields: optional comma-separated field names to retrieve
        :param int page: optional page number for paginated bulk lookups
        :param int page_size: optional page size for paginated bulk lookups
        :return: response dict
        """
        payload: dict[str, Any] = {}
        url_suffix = f"ingestion/openapi/bulk-lookup/{object_type}/"
        client_url = self.base_url + url_suffix
        params: dict[str, Any] = {
            "enrichment_data": enrichment_data,
            "relation_data": relation_data,
        }
        if enrichment_tools:
            params["enrichment_tools"] = enrichment_tools
        if fields:
            params["fields"] = fields
        if page is not None:
            params["page"] = page
        if page_size is not None:
            params["page_size"] = page_size
        if values:
            payload.update({"value": values})
        elif object_ids:
            payload.update({"object_id": object_ids})
        else:
            return_error("Either values or object_ids must be provided for bulk IOC lookup.")
        return self.post_http_request(client_url, payload, params)

    def get_vulnerability_product_details(self, obj_id: str, page: int, page_size: int):
        """
        Get Vulnerability Product Details

        :param str obj_id: The ID of the vulnerability to get the product details for
        :param int page: Paginated number from where data will be polled
        :param int page_size: Size of the result
        :return dict: Returns response for query
        """
        url_suffix = f"ingestion/threat-data/vulnerability/{obj_id}/product-details/"
        client_url = self.base_url + url_suffix
        params = {"page": page, "page_size": page_size}
        return self.get_http_request(client_url, {}, None, params=params)

    def get_vulnerability_cvss_score(self, obj_id: str, page: int, page_size: int):
        """
        Get Vulnerability CVSS Score

        :param str obj_id: The ID of the vulnerability to get the product details for
        :param int page: Paginated number from where data will be polled
        :param int page_size: Size of the result
        :return dict: Returns response for query
        """
        url_suffix = f"ingestion/threat-data/vulnerability/{obj_id}/cvss-score/"
        client_url = self.base_url + url_suffix
        params = {"page": page, "page_size": page_size}
        return self.get_http_request(client_url, {}, None, params=params)

    def get_vulnerability_source_description(self, obj_id: str, source_id: str, page: int, page_size: int):
        """
        Get Vulnerability Source Description

        :param str obj_id: The ID of the vulnerability to get the source description for
        :param str source_id: The ID of the source to get the description for
        :param int page: Paginated number from where data will be polled
        :param int page_size: Size of the result
        :return dict: Returns response for query
        """
        url_suffix = f"ingestion/threat-data/vulnerability/{obj_id}/source-description/"
        client_url = self.base_url + url_suffix
        params = {"source_id": source_id, "page": page, "page_size": page_size}
        return self.get_http_request(client_url, {}, None, params=params)


""" HELPER FUNCTIONS """


def execute_with_retry(func: Callable, *args, **kwargs) -> Any:
    """
    Executes a function with a single retry after 60 seconds if it fails.
    """
    global RETRY_COUNT
    try:
        return func(*args, **kwargs)
    except DemistoException as e:
        if "429" not in str(e):
            raise e
        RETRY_COUNT += 1
        if RETRY_COUNT > 3:
            demisto.error(f"CTIX: RETRY_COUNT exceeded {RETRY_COUNT}. Error: {e}")
            raise e
        demisto.error(f"CTIX: Hit an error: {e}. Waiting 60 seconds before retrying... (Retry {RETRY_COUNT})")
        time.sleep(60)  # pylint: disable=E9003
        return func(*args, **kwargs)


def to_dbot_score(ctix_score: int) -> int:
    """
    Maps CTIX Score to DBotScore
    """
    if isinstance(ctix_score, str):
        try:
            ctix_score = int(ctix_score)
        except (ValueError, TypeError):
            ctix_score = 0
    if ctix_score == 0:
        dbot_score = Common.DBotScore.NONE  # unknown
    elif ctix_score <= 30:
        dbot_score = Common.DBotScore.GOOD  # good
    elif ctix_score <= 70:
        dbot_score = Common.DBotScore.SUSPICIOUS  # suspicious
    else:
        dbot_score = Common.DBotScore.BAD
    return dbot_score


def no_result_found(data: Any):
    if data in ("", " ", None, [], {}):
        result = CommandResults(
            readable_output="No results were found",
            outputs=None,
            raw_response=None,
        )
    else:
        result = data
    return result


def check_for_empty_variable(value: str, default: Any):
    return value if value not in ("", " ", None) else default


def iter_dbot_score(
    data: list,
    score_key: str,
    type_key: str,
    table_name: str,
    output_prefix: str,
    outputs_key_field: str,
    reliability: str = None,
):
    final_data = []
    for value in data:
        if value[type_key] is not None:
            indicator_type = CTIX_DBOT_MAP[value[type_key]]
            score = to_dbot_score(value.get(score_key, 0))
            if indicator_type == "ip":
                dbot_score = Common.DBotScore(
                    indicator=value.get("name"),
                    indicator_type=DBotScoreType.IP,
                    integration_name="CTIX",
                    score=score,
                    reliability=reliability,
                )
                ip_standard_context = Common.IP(ip=value.get("name"), asn=value.get("asn"), dbot_score=dbot_score)
                final_data.append(
                    CommandResults(
                        readable_output=tableToMarkdown(table_name, value, removeNull=True),
                        outputs_prefix=output_prefix,
                        outputs_key_field=outputs_key_field,
                        outputs=value,
                        indicator=ip_standard_context,
                        raw_response=value,
                    )
                )
            elif indicator_type == "file":
                dbot_score = Common.DBotScore(
                    indicator=value.get("name"),
                    indicator_type=DBotScoreType.FILE,
                    integration_name="CTIX",
                    score=score,
                    reliability=reliability,
                )
                file_standard_context = Common.File(name=value.get("name"), dbot_score=dbot_score)
                file_key = value.get("name")
                hash_type = value.get("attribute_field", "Unknown").lower()
                if hash_type == "md5":
                    file_standard_context.md5 = file_key
                elif hash_type == "sha-1":
                    file_standard_context.sha1 = file_key
                elif hash_type == "sha-256":
                    file_standard_context.sha256 = file_key
                elif hash_type == "sha-512":
                    file_standard_context.sha512 = file_key

                final_data.append(
                    CommandResults(
                        readable_output=tableToMarkdown(table_name, value, removeNull=True),
                        outputs_prefix=output_prefix,
                        outputs_key_field=outputs_key_field,
                        outputs=value,
                        indicator=file_standard_context,
                        raw_response=value,
                    )
                )
            elif indicator_type == "domain":
                dbot_score = Common.DBotScore(
                    indicator=value.get("name"),
                    indicator_type=DBotScoreType.DOMAIN,
                    integration_name="CTIX",
                    score=score,
                    reliability=reliability,
                )
                domain_standard_context = Common.Domain(domain=value.get("name"), dbot_score=dbot_score)
                final_data.append(
                    CommandResults(
                        readable_output=tableToMarkdown(table_name, value, removeNull=True),
                        outputs_prefix=output_prefix,
                        outputs_key_field=outputs_key_field,
                        outputs=value,
                        indicator=domain_standard_context,
                        raw_response=value,
                    )
                )
            elif indicator_type == "email":
                dbot_score = Common.DBotScore(
                    indicator=value.get("name"),
                    indicator_type=DBotScoreType.EMAIL,
                    integration_name="CTIX",
                    score=score,
                    reliability=reliability,
                )
                email_standard_context = Common.Domain(domain=value.get("name"), dbot_score=dbot_score)
                final_data.append(
                    CommandResults(
                        readable_output=tableToMarkdown(table_name, value, removeNull=True),
                        outputs_prefix=output_prefix,
                        outputs_key_field=outputs_key_field,
                        outputs=value,
                        indicator=email_standard_context,
                        raw_response=value,
                    )
                )
            elif indicator_type == "url":
                dbot_score = Common.DBotScore(
                    indicator=value.get("name"),
                    indicator_type=DBotScoreType.URL,
                    integration_name="CTIX",
                    score=score,
                    reliability=reliability,
                )
                url_standard_context = Common.URL(url=value.get("name"), dbot_score=dbot_score)
                final_data.append(
                    CommandResults(
                        readable_output=tableToMarkdown(table_name, value, removeNull=True),
                        outputs_prefix=output_prefix,
                        outputs_key_field=outputs_key_field,
                        outputs=value,
                        indicator=url_standard_context,
                        raw_response=value,
                    )
                )
            else:  # indicator_type == 'custom'
                final_data.append(
                    CommandResults(
                        readable_output=tableToMarkdown(table_name, value, removeNull=True),
                        outputs_prefix=output_prefix,
                        outputs_key_field=outputs_key_field,
                        outputs=value,
                        raw_response=value,
                    )
                )
        else:
            final_data.append(
                CommandResults(
                    readable_output=tableToMarkdown(table_name, value, removeNull=True),
                    outputs_prefix=output_prefix,
                    outputs_key_field=outputs_key_field,
                    outputs=value,
                    raw_response=value,
                )
            )
    return final_data


def _normalize_timestamp_to_iso(ts: Any) -> str | None:
    """Convert a timestamp value to ISO8601 format.

    Handles epoch int/float, ISO strings, and other common date string formats.
    Returns None if the value cannot be parsed.
    """
    if ts is None:
        return None
    # Epoch int/float
    if isinstance(ts, int | float) and ts > 0:
        return datetime.fromtimestamp(ts, tz=UTC).strftime(FETCH_DATE_FORMAT)
    # Already a string – try to parse
    if isinstance(ts, str) and ts.strip():
        parsed = dateparser.parse(ts)
        if parsed:
            return parsed.strftime(FETCH_DATE_FORMAT)
    return None


def _normalize_cyware_indicator_type(raw_type: str | None) -> str:
    """Normalize a Cyware indicator type string to Cortex XSOAR type."""
    DEFAULT_INDICATOR = "Indicator"
    if not raw_type:
        return DEFAULT_INDICATOR
    lookup = raw_type.strip().lower().replace(" ", "_")
    return CYWARE_TYPE_NORMALIZATION.get(
        lookup, INDICATOR_TYPE_MAP.get(raw_type, _STIX_SDO_TO_XSOAR_ENTITY_TYPE.get(raw_type, DEFAULT_INDICATOR))
    )


def confidence_to_dbot_score(confidence: Any) -> int:
    """Map CTIX confidence score to DBotScore.

    confidence >= 80 -> Malicious (3)
    confidence 50-79 -> Suspicious (2)
    confidence < 50  -> Unknown (1)
    None / non-numeric -> None (0)
    """
    if confidence is None:
        return Common.DBotScore.NONE
    if isinstance(confidence, str):
        try:
            confidence = float(confidence)
        except (ValueError, TypeError):
            return Common.DBotScore.NONE
    if confidence >= 80:
        return Common.DBotScore.BAD
    if confidence >= 50:
        return Common.DBotScore.SUSPICIOUS
    return Common.DBotScore.NONE


def normalize_indicator_type(ctix_ioc_type: str | None) -> str:
    """Normalize CTIX IOC type to XSOAR FeedIndicatorType."""
    if not ctix_ioc_type:
        return "Custom Indicator"
    return INDICATOR_TYPE_MAP.get(ctix_ioc_type, "Custom Indicator")


def map_report_severity(report: dict) -> int:
    """Convert CTIX report severity/confidence to XSOAR severity (1-4)."""
    severity_str = (report.get("risk_severity") or "").upper()
    severity_map = {
        "LOW": IncidentSeverity.LOW,
        "MEDIUM": IncidentSeverity.MEDIUM,
        "HIGH": IncidentSeverity.HIGH,
        "CRITICAL": IncidentSeverity.CRITICAL,
    }
    if severity_str in severity_map:
        return severity_map[severity_str]

    confidence = report.get("confidence_score")
    if isinstance(confidence, str):
        try:
            confidence = float(confidence)
        except (ValueError, TypeError):
            confidence = None
    if isinstance(confidence, int | float):
        if confidence >= 80:
            return IncidentSeverity.HIGH
        if confidence >= 50:
            return IncidentSeverity.MEDIUM
        if confidence >= 20:
            return IncidentSeverity.LOW

    return IncidentSeverity.UNKNOWN


def map_report_to_incident(report: dict, relations: dict | None = None, source_reliability: str = "") -> dict:
    """Map a CTIX report object to an XSOAR incident dict."""
    report_id = report.get("id", "")
    title = report.get("name")
    created_ts = report.get("created")
    occurred = ""
    if isinstance(created_ts, int | float) and created_ts > 0:
        occurred = datetime.fromtimestamp(created_ts, tz=UTC).strftime(FETCH_DATE_FORMAT)

    tags = report.get("tags", [])
    labels = []
    if isinstance(tags, list):
        for tag in tags:
            if isinstance(tag, dict):
                labels.append({"type": "Tag", "value": tag.get("name", "")})
            elif isinstance(tag, str):
                labels.append({"type": "Tag", "value": tag})

    # Build XSOAR relationships from the fetched relations dict
    relationships: list[dict] = []
    if relations and isinstance(relations, dict):
        relationships = _build_relationships(
            indicator_value=title or report_id,
            xsoar_type=ThreatIntel.ObjectsNames.REPORT,
            relations=relations,
            source_reliability=source_reliability,
        )

    # Build per-field CustomFields mapping
    custom_fields: dict[str, Any] = {}
    if report.get("custom_scores"):
        custom_fields["ctixcustomscores"] = json.dumps(report["custom_scores"])
    if report.get("custom_attributes"):
        custom_attribute_data = {
            "custom_attributes": report.get("custom_attributes", []),
            "custom_scores": report.get("custom_scores", {}),
        }
        custom_fields["ctixcustomattributes"] = json.dumps(custom_attribute_data)
    if relations:
        custom_fields["ctixrelations"] = json.dumps(relations)

    incident: dict[str, Any] = {
        "name": f"CTIX Intel: {title}",
        "occurred": occurred,
        "severity": map_report_severity(report),
        "labels": labels,
        "dbotMirrorId": report_id,
        "rawJSON": json.dumps(report),
        "CustomFields": custom_fields,
    }
    if relationships:
        incident["relationships"] = relationships
    return incident


def _severity_to_dbot_score(severity: str | None) -> int:
    """Translate Cyware severity string to XSOAR DBot score.

    Mapping:
        HIGH / CRITICAL -> 3 (Bad)
        MEDIUM          -> 2 (Suspicious)
        LOW / UNKNOWN   -> 1 (Good)
        None / other    -> 0 (None)
    """
    if not severity:
        return Common.DBotScore.NONE
    severity_upper = severity.strip().upper()
    if severity_upper in ("HIGH", "CRITICAL"):
        return Common.DBotScore.BAD
    if severity_upper == "MEDIUM":
        return Common.DBotScore.SUSPICIOUS
    if severity_upper in ("LOW", "UNKNOWN"):
        return Common.DBotScore.GOOD
    return Common.DBotScore.NONE


def _extract_tag_names(tags: Any) -> list[str]:
    """Extract a flat list of tag name strings from various tag formats."""
    tag_names: list[str] = []
    if not isinstance(tags, list):
        return tag_names
    for tag in tags:
        if isinstance(tag, dict):
            name = tag.get("name", "")
            if name:
                tag_names.append(name)
        elif isinstance(tag, str) and tag:
            tag_names.append(tag)
    return tag_names


def _extract_source_names(sources: Any) -> list[str]:
    """Extract a flat list of source name strings."""
    source_names: list[str] = []
    if not isinstance(sources, list):
        return source_names
    for src in sources:
        if isinstance(src, dict):
            name = src.get("name", "")
            if name:
                source_names.append(name)
        elif isinstance(src, str) and src:
            source_names.append(src)
    return source_names


def _build_relationships(
    indicator_value: str,
    xsoar_type: str,
    relations: Any,
    source_reliability: str = "",
) -> list[dict]:
    """Build XSOAR relationship dicts from Cyware relations payload.

    CTIX returns two relation formats depending on the source:

    1. **Enrichment / dict format** — key is the relationship verb, value items are dicts:
       ``{"related-to": [{"name": "evil.com", "type": "domain-name"}]}``

    2. **Base / string format** — key is the STIX target object type, value items are names:
       ``{"malware": ["Adaptix"], "report": ["Feed 6"], "threat-actor": ["APT29"]}``

    In format (2) the key is NOT a valid XSOAR relationship name, so we always use
    ``"related-to"`` as the relationship verb and derive entity_b_type from the key.

    Args:
        indicator_value: The indicator value (entity_a).
        xsoar_type: The XSOAR type of entity_a.
        relations: Relations dict as described above.
        source_reliability: Source reliability string from integration params.

    Returns:
        List of relationship dicts produced by EntityRelationship.to_indicator().
    """
    relationships: list[dict] = []
    if not isinstance(relations, dict):
        return relationships
    for rel_type, rel_list in relations.items():
        if not isinstance(rel_list, list):
            continue
        for rel in rel_list:
            if isinstance(rel, dict):
                # Enrichment format: the dict key IS the relationship verb.
                # Validate it; fall back to "related-to" for any unknown verbs.
                rel_name = rel.get("name", "")
                raw_target_type = rel.get("type", "")
                rel_target_type = _STIX_SDO_TO_XSOAR_ENTITY_TYPE.get(
                    raw_target_type,
                    _normalize_cyware_indicator_type(raw_target_type),
                )
                relationship_verb = (
                    rel_type
                    if EntityRelationship.Relationships.is_valid(rel_type)
                    else EntityRelationship.Relationships.RELATED_TO
                )
            else:
                # Base format: the dict key is the STIX object TYPE of the target
                # (e.g. "malware", "report"), NOT a relationship verb.
                # Always use "related-to" and derive entity_b_type from the key.
                rel_name = str(rel)
                rel_target_type = _STIX_SDO_TO_XSOAR_ENTITY_TYPE.get(rel_type, "Indicator")
                relationship_verb = EntityRelationship.Relationships.RELATED_TO
            if not rel_name:
                continue
            relationships.append(
                EntityRelationship(
                    name=relationship_verb,
                    entity_a=indicator_value,
                    entity_a_type=xsoar_type,
                    entity_b=rel_name,
                    entity_b_type=rel_target_type,
                    source_reliability=source_reliability,
                    brand="Cyware Intel Exchange",
                ).to_indicator()
            )
    return relationships


def _hash_field_name(raw_type: str, attribute_field: str = "") -> str | None:
    """Return the XSOAR hash field name ('md5', 'sha1', 'sha256', …) for a raw type string.

    Checks ``_HASH_TYPE_TO_FIELD`` by exact key first, then by a normalised lowercase
    key, then falls back to ``attribute_field`` (used by the saved-result-set schema
    where ``indicator_type`` is a dict carrying the real attribute name).
    Returns ``None`` when the type is not a file hash.
    """
    if raw_type in _HASH_TYPE_TO_FIELD:
        return _HASH_TYPE_TO_FIELD[raw_type]
    normalised = (raw_type or "").strip().lower().replace(" ", "_")
    if normalised in _HASH_TYPE_TO_FIELD:
        return _HASH_TYPE_TO_FIELD[normalised]
    if attribute_field:
        attr_norm = attribute_field.strip().lower().replace("-", "").replace("_", "")
        return _HASH_TYPE_TO_FIELD.get(attr_norm)
    return None


def parse_cyware_indicator(
    cyware_data: dict, source_reliability: str = "", feed_tags: list | None = None, tlp_color: str = ""
) -> dict:
    """Map a single Cyware/CTIX indicator dict to an XSOAR feed indicator object.

    Strict mapping (per requirements):
        first_seen / first seen  -> fields.firstseenbysource
        last_seen / last seen    -> fields.lastseenbysource
        tags / Tags              -> fields.tags  (list[str])
        tlp / TLP / source_tlp   -> fields.trafficlightprotocol
        confidence_score / ctix_score / Confidence Score -> fields.confidence (int)
        severity                 -> fields.threatassessscore AND top-level score
        custom_attributes        -> fields.cywarecustomattribute<name>
        relations                -> relationships (via EntityRelationship.to_indicator())
        rawJSON                  -> combined raw dict (base + enrichment)
    """
    indicator_value: str = cyware_data.get("name") or cyware_data.get("sdo_name") or ""
    raw_type = cyware_data.get("ioc_type") or cyware_data.get("indicator_type", "")
    if isinstance(raw_type, dict):
        raw_type = raw_type.get("type", "")
    xsoar_type = _normalize_cyware_indicator_type(raw_type)
    severity = cyware_data.get("severity")
    dbot_score = (
        _severity_to_dbot_score(severity)
        if severity
        else confidence_to_dbot_score(cyware_data.get("confidence_score") or cyware_data.get("ctix_score"))
    )
    confidence = cyware_data.get("confidence_score") or cyware_data.get("ctix_score")
    if isinstance(confidence, str):
        try:
            confidence = float(confidence)
        except (ValueError, TypeError):
            confidence = None
    tag_names = _extract_tag_names(cyware_data.get("tags"))
    if feed_tags:
        tag_names.extend(feed_tags)
    custom_fields = {}
    source_names = _extract_source_names(cyware_data.get("sources", []))
    relationships = _build_relationships(indicator_value, xsoar_type, cyware_data.get("relations", {}), source_reliability)
    custom_attributes = {
        "custom_attributes": cyware_data.get("custom_attributes", {}),
        "custom_scores": cyware_data.get("custom_scores", {}),
    }
    if custom_attributes["custom_attributes"] or custom_attributes["custom_scores"]:
        custom_fields = {"ctixcustomattributes": json.dumps(custom_attributes)}
    # _normalize_cyware_indicator_type already set xsoar_type to File for hash types;
    # _hash_field_name just tells us which specific field (md5/sha1/sha256) to populate.
    hash_field = _hash_field_name(raw_type)
    hash_fields: dict[str, str] = {hash_field: indicator_value} if hash_field else {}

    fields: dict[str, Any] = assign_params(
        firstseenbysource=_normalize_timestamp_to_iso(cyware_data.get("ctix_created")),
        lastseenbysource=_normalize_timestamp_to_iso(cyware_data.get("ctix_modified")),
        trafficlightprotocol=tlp_color
        or cyware_data.get("source_tlp")
        or cyware_data.get("ctix_tlp")
        or cyware_data.get("analyst_tlp"),
        tags=tag_names if tag_names else None,
        confidence=confidence,
        threatassessscore=cyware_data.get("severity"),
        description=cyware_data.get("description") or None,
        reportedby=", ".join(source_names) if source_names else None,
        ctixid=cyware_data.get("id") or None,
        # Flags arriving as False are kept (assign_params only drops None/empty),
        # so un-flagging in CTIX clears a previously-True TIM field on next fetch.
        ctixisfalsepositive=cyware_data.get("is_false_positive"),
        ctixisdeprecated=cyware_data.get("is_deprecated"),
        ctixisreviewed=cyware_data.get("is_reviewed"),
        ctixiswhitelisted=cyware_data.get("is_whitelist"),
        ctixisrevoked=cyware_data.get("is_revoked"),
    )
    if hash_fields:
        fields.update(hash_fields)
    if cyware_data.get("custom_scores"):
        custom_fields["ctixcustomscores"] = json.dumps(cyware_data["custom_scores"])

    # --- enrichment extra data ---
    enrich_objects = cyware_data.get("enrichment_data", [])
    if isinstance(enrich_objects, list) and enrich_objects:
        custom_fields["ctixenrichment"] = json.dumps(enrich_objects)
    if custom_fields:
        fields.update(custom_fields)
    # --- build indicator object ---
    # rawJSON must always be a plain dict. XSOAR's Go runtime expects
    # map[string]interface{} and will raise a ValueError if it receives a
    # JSON-encoded string instead (e.g. if cyware_data were somehow a str).
    raw_json: dict = cyware_data if isinstance(cyware_data, dict) else {}
    xsoar_indicator: dict[str, Any] = assign_params(
        value=indicator_value,
        type=xsoar_type,
        score=dbot_score,
        rawJSON=raw_json,
        fields=fields,
        relationships=relationships if relationships else None,
    )
    return xsoar_indicator


def map_ctix_indicator_to_xsoar(indicator: dict, reliability: str, enrichment_data: dict | None = None) -> dict:
    """Backward-compatible wrapper around parse_cyware_indicator.

    Merges enrichment data into the indicator dict before parsing and threads
    the source reliability through to EntityRelationship construction.
    """
    if enrichment_data:
        merged = dict(indicator)
        # Merge enrichment fields that are missing in base data
        for key in (
            "description",
            "relations",
            "enrichment_data",
            "custom_attributes",
            "confidence_score",
            "tlp",
            "first_seen",
            "last_seen",
        ):
            if enrichment_data.get(key) and not merged.get(key):
                merged[key] = enrichment_data[key]
        return parse_cyware_indicator(merged, source_reliability=reliability)
    return parse_cyware_indicator(indicator, source_reliability=reliability)


""" FETCH FUNCTIONS """


def _state_key(prefix: str, key: str) -> str:
    """Build a namespaced LastRun key for multi-instance support."""
    instance_name = demisto.integrationInstance() or "default"
    return f"{prefix}_{key}_{instance_name}"


def _parse_fetch_interval_to_minutes(fetch_interval: Any) -> int:
    """Parse feed/fetch interval values into minutes, with safe fallbacks."""
    if fetch_interval in (None, "", " "):
        return 60

    if isinstance(fetch_interval, int | float):
        return max(1, int(fetch_interval))

    raw = str(fetch_interval).strip().lower()
    if raw.isdigit():
        return max(1, int(raw))

    interval_match = re.match(r"^(\d+)\s*([a-z]+)$", raw)
    if not interval_match:
        # Try dateparser for values like "12 hours"
        now = datetime.now(UTC)
        parsed = dateparser.parse(raw, settings={"RELATIVE_BASE": now})
        if parsed:
            delta_seconds = int((parsed - now).total_seconds())
            return max(1, delta_seconds // 60)
        return 60

    value = int(interval_match.group(1))
    unit = interval_match.group(2)
    if unit.startswith("min"):
        return max(1, value)
    if unit.startswith("hour"):
        return max(1, value * 60)
    if unit.startswith("day"):
        return max(1, value * 1440)
    return max(1, value)


def _derive_iteration_threshold(interval_minutes: int) -> int:
    """Derive max pages per run from interval, per design plan."""
    if interval_minutes < 60:
        return 5
    if interval_minutes < 300:
        return 10
    return 25


def _resolve_initial_fetch_from_timestamp(from_timestamp: int, page_number: int, params: dict) -> int:
    """When LastRun has no watermark (0) and we start at page 1, use first_fetch minutes from params."""
    if from_timestamp != 0 or page_number != 1:
        return from_timestamp
    minutes = arg_to_number(params.get("first_fetch"))
    if minutes is None or minutes <= 0:
        minutes = FIRST_FETCH_DEFAULT_MINUTES
    return int(datetime.now(UTC).timestamp()) - int(minutes) * 60


def _load_fetch_state(last_run: dict, prefix: str) -> tuple[int, int, dict]:
    """Read resumable pagination state from LastRun."""
    page_key = _state_key(prefix, "page_number")
    ts_key = _state_key(prefix, "last_run_date")

    from_timestamp = arg_to_number(last_run.get(ts_key))
    if from_timestamp is None:
        from_timestamp = arg_to_number(last_run.get("last_run_date"))

    page_number = arg_to_number(last_run.get(page_key))
    if page_number is None:
        page_number = arg_to_number(last_run.get("page_number"))
    if page_number in (None, 0):
        page_number = 1

    safe_from_timestamp = int(from_timestamp) if from_timestamp is not None else 0
    safe_page_number = int(page_number) if page_number is not None else 1
    return safe_from_timestamp, safe_page_number, {"page_key": page_key, "ts_key": ts_key}


def _store_partial_fetch_state(last_run: dict, keys: dict, page_number: int, from_timestamp: int) -> dict:
    """Store partial-run state and preserve unrelated LastRun keys."""
    next_run = dict(last_run)
    next_run[keys["page_key"]] = page_number
    next_run[keys["ts_key"]] = from_timestamp
    # Compatibility keys for easier troubleshooting.
    next_run["page_number"] = page_number
    next_run["last_run_date"] = from_timestamp
    if FETCH_INCIDENTS_STATE_PREFIX in keys["page_key"]:
        next_run["last_fetch_time"] = from_timestamp
    if FETCH_INDICATORS_STATE_PREFIX in keys["page_key"]:
        next_run["last_indicator_time"] = from_timestamp
    return next_run


def _store_completed_fetch_state(last_run: dict, keys: dict) -> dict:
    """Store full-sweep completion state."""
    next_run = dict(last_run)
    completed_ts = int(time.time()) - DELTA_TIME_DIFF
    next_run[keys["page_key"]] = 0
    next_run[keys["ts_key"]] = completed_ts
    # Compatibility keys for easier troubleshooting.
    next_run["page_number"] = 0
    next_run["last_run_date"] = completed_ts
    if FETCH_INCIDENTS_STATE_PREFIX in keys["page_key"]:
        next_run["last_fetch_time"] = completed_ts
    if FETCH_INDICATORS_STATE_PREFIX in keys["page_key"]:
        next_run["last_indicator_time"] = completed_ts
    return next_run


def fetch_incidents(client: Client, params: dict, last_run: dict) -> tuple[dict, list]:
    """Fetch CTIX reports as XSOAR incidents.

    :param Client client: CTIX API client
    :param dict params: Integration instance parameters
    :param dict last_run: Previous run state from demisto.getLastRun()
    :return tuple: (next_run dict, list of incident dicts)
    """
    source_reliability = params.get("integrationReliability", "")

    max_fetch = arg_to_number(params.get("max_fetch"))
    if max_fetch is None:
        max_fetch = 10
    max_fetch = min(max(1, int(max_fetch)), 200)

    from_timestamp, page_number, state_keys = _load_fetch_state(
        last_run=last_run,
        prefix=FETCH_INCIDENTS_STATE_PREFIX,
    )
    from_timestamp = _resolve_initial_fetch_from_timestamp(from_timestamp, page_number, params)

    demisto.debug(f"CTIX fetch_incidents: from_timestamp={from_timestamp}, page_number={page_number}, max_fetch={max_fetch}")

    incidents: list[dict] = []

    custom_query = (params.get("incident_fetch_query") or "").strip() or 'type = "report"'
    final_query = f'{custom_query} AND ctix_modified >= "{from_timestamp}"'
    report_query = final_query
    demisto.debug(f"CTIX fetch_incidents: using CQL query: {report_query}")

    try:
        response = execute_with_retry(client.get_threat_data, page=page_number, page_size=max_fetch, query=report_query)
    except DemistoException as e:
        demisto.error(f"CTIX fetch_incidents: DemistoException while fetching incidents: {e}")
        return _store_partial_fetch_state(last_run, state_keys, page_number, from_timestamp), incidents

    data = response.get("data", {}) if response else {}
    results = data.get("results", []) or []
    results = results[:max_fetch]
    next_page_val: str | None = data.get("next") if isinstance(data.get("next"), str) else None
    relation_enrichment_rate_limit_failure = False

    relations_by_id: dict[str, dict] = {}
    if results:
        type_groups: dict[str, list[str]] = {}
        for report in results:
            rid = report.get("id") or ""
            sdo_type = report.get("type")
            if rid and sdo_type:
                type_groups.setdefault(sdo_type, []).append(rid)

        try:
            for object_type, ids_list in type_groups.items():
                for i in range(0, len(ids_list), ENRICHMENT_BATCH_SIZE):
                    chunk = ids_list[i : i + ENRICHMENT_BATCH_SIZE]
                    enriched_rows = _bulk_ioc_lookup_advanced_collect_all_pages(
                        client,
                        object_type=object_type,
                        values=[],
                        object_ids=chunk,
                        enrichment_data=False,
                        relation_data=True,
                        enrichment_tools=None,
                        fields=None,
                    )
                    for row in enriched_rows:
                        oid = row.get("id", "")
                        if oid:
                            relations_by_id[oid] = row.get("relations") or {}
        except DemistoException as e:
            if RATE_LIMIT_STATUS_ERR in str(e):
                relation_enrichment_rate_limit_failure = True
                demisto.error(
                    f"CTIX fetch_incidents: Rate limit hit again after retry during relation enrichment: {e}. "
                    "Continuing with base threat-data results; incidents will be mapped without full relations."
                )
            else:
                demisto.debug(f"CTIX fetch_incidents: Bulk relations lookup failed: {e}")
        except Exception as e:
            demisto.debug(f"CTIX fetch_incidents: Bulk relations lookup failed: {e}")

        for report in results:
            report_id = report.get("id", "")
            relations_data = relations_by_id.get(report_id, {}) if report_id else {}
            incident = map_report_to_incident(report, relations=relations_data, source_reliability=source_reliability)
            incidents.append(incident)
    else:
        demisto.debug("CTIX fetch_incidents: No results on this page")

    if relation_enrichment_rate_limit_failure:
        if next_page_val:
            demisto.error(
                f"CTIX fetch_incidents: Relation enrichment failed (rate limit after retries). "
                f"Returning {len(incidents)} incident(s) from base fetch without full relations. "
                f"Checkpoint: threat-data page_number={page_number}, next run will use page_number={page_number + 1}."
            )
        else:
            demisto.error(
                f"CTIX fetch_incidents: Relation enrichment failed (rate limit after retries). "
                f"Returning {len(incidents)} incident(s) from base fetch without full relations. "
                f"Checkpoint: completing sweep for this interval (no further threat-data pages); LastRun will advance."
            )

    if next_page_val:
        return _store_partial_fetch_state(last_run, state_keys, page_number + 1, from_timestamp), incidents

    return _store_completed_fetch_state(last_run, state_keys), incidents


def _collect_saved_result_set_indicators(
    client: Client,
    from_timestamp: int,
    page_number: int,
    iteration_threshold: int,
    label_name: str | None,
    version: str | None,
    to_timestamp: int,
) -> tuple[list[dict], int, bool]:
    """Paginate through the saved result set endpoint and collect raw indicator dicts.

    Uses the existing ``client.saved_result_set()`` method.
    All object types returned by the API are collected; type normalisation into the
    correct XSOAR indicator type is handled downstream by ``parse_cyware_indicator``.
    Deduplication is intentionally delegated to ``demisto.createIndicators`` at submission time.
    """
    all_indicators: list[dict] = []
    page_size = PAGE_SIZE
    current_page_number = page_number
    pages_processed = 0

    try:
        response = execute_with_retry(
            client.saved_result_set,
            page=current_page_number,
            page_size=page_size,
            label_name=label_name,
            version=version,
            from_timestamp=from_timestamp,
            to_timestamp=to_timestamp,
        )
    except DemistoException as e:
        demisto.error(f"CTIX fetch_indicators: Error at initial request: {e}")
        return all_indicators, current_page_number, False

    while response and pages_processed < iteration_threshold:
        data = response.get("data", {}) if response else {}
        result_sets = data.get("results", [])

        if not result_sets:
            demisto.debug("CTIX fetch_indicators: No more results")
            return all_indicators, current_page_number, True

        for result_set in result_sets:
            indicators_data = result_set.get("data", [])
            if not indicators_data:
                continue
            for ind in indicators_data:
                all_indicators.append(ind)

        # Move to the next page for pagination
        next_url = data.get("next")
        pages_processed += 1
        if not next_url:
            return all_indicators, current_page_number, True

        current_page_number += 1
        if pages_processed >= iteration_threshold:
            demisto.debug(
                f"CTIX fetch_indicators: Iteration threshold reached at page_number={current_page_number}, "
                "saving checkpoint for next run"
            )
            return all_indicators, current_page_number, False

        try:
            response = execute_with_retry(
                client.saved_result_set,
                page=current_page_number,
                page_size=page_size,
                label_name=label_name,
                version=version,
                from_timestamp=from_timestamp,
                to_timestamp=to_timestamp,
            )
        except DemistoException as e:
            if RATE_LIMIT_STATUS_ERR in str(e):
                demisto.error(f"CTIX fetch_indicators: Rate limit hit again during pagination: {e}")
                return all_indicators, current_page_number, False
            demisto.error(f"CTIX fetch_indicators: Error following next page: {e}")
            return all_indicators, current_page_number, False

    return all_indicators, current_page_number, False


def _merge_enrichment_into_indicators(
    base_indicators: list[dict],
    enrichment_map: dict[str, dict],
) -> list[dict]:
    """Merge enrichment data (custom_attributes, relations, etc.) into base indicator dicts.

    Returns a new list of merged dicts; original dicts are not mutated.
    """
    merged: list[dict] = []
    for ind in base_indicators:
        ind_id = ind.get("id") or ""
        enrich = enrichment_map.get(ind_id)
        if enrich:
            combined = dict(ind)
            # Merge keys from enrichment that add new information
            # Fill missing fields from enrichment; relations/custom_attributes handled below
            for key in (
                "description",
                "enrichment_data",
                "confidence_score",
                "tlp",
                "first_seen",
                "last_seen",
                "country",
            ):
                if enrich.get(key) and not combined.get(key):
                    combined[key] = enrich[key]
            # Always prefer enrichment relations/custom_attributes when present
            if enrich.get("relations"):
                combined["relations"] = enrich["relations"]
            if enrich.get("custom_attributes"):
                combined["custom_attributes"] = enrich["custom_attributes"]
            merged.append(combined)
        else:
            merged.append(ind)
    return merged


def fetch_indicators(client: Client, params: dict, last_run: dict) -> tuple[dict, list]:
    """Fetch indicators from CTIX saved result sets.

    Flow:
        1. Collect base IOC data via the existing ``client.saved_result_set()`` method.
        2. If ``retrieve_enriched_data`` (if_enrich) is True, enrich via bulk IOC lookup.
        3. Merge enrichment data back into the base IOC dicts.
        4. Map each merged dict to an XSOAR indicator via ``parse_cyware_indicator``.
        5. Return (next_run, indicators). Caller is responsible for ``demisto.createIndicators``.

    :param Client client: CTIX API client
    :param dict params: Integration instance parameters
    :param dict last_run: Previous run state from demisto.getLastRun()
    :return tuple: (next_run dict, list of XSOAR indicator dicts)
    """
    label_name = params.get("saved_result_set_label")
    version = params.get("saved_result_set_version")
    if_enrich: bool = argToBoolean(params.get("retrieve_enriched_data", False))
    feed_tags = argToList(params.get("feedTags"))
    tlp_color: str = params.get("tlp_color", "") or ""

    from_timestamp, page_number, state_keys = _load_fetch_state(
        last_run=last_run,
        prefix=FETCH_INDICATORS_STATE_PREFIX,
    )
    from_timestamp = _resolve_initial_fetch_from_timestamp(from_timestamp, page_number, params)
    fetch_interval_minutes = _parse_fetch_interval_to_minutes(params.get("feedFetchInterval"))
    iteration_threshold = _derive_iteration_threshold(fetch_interval_minutes)

    demisto.debug(
        f"CTIX fetch_indicators: from_timestamp={from_timestamp}, page_number={page_number}, "
        f"iteration_threshold={iteration_threshold}"
    )

    current_time = int(datetime.now(UTC).timestamp())

    # Step 1: Collect base IOC data using existing saved_result_set action
    all_indicators_raw, next_page_number, completed_sweep = _collect_saved_result_set_indicators(
        client=client,
        from_timestamp=from_timestamp,
        page_number=page_number,
        iteration_threshold=iteration_threshold,
        label_name=label_name,
        version=version,
        to_timestamp=current_time,
    )
    demisto.debug(f"CTIX fetch_indicators: Collected {len(all_indicators_raw)} raw indicators")

    # Step 2 & 3: Optionally enrich and merge
    if if_enrich and all_indicators_raw:
        enrichment_map = enrich_indicators_bulk(client, all_indicators_raw)
        if enrichment_map:
            all_indicators_raw = _merge_enrichment_into_indicators(all_indicators_raw, enrichment_map)
            demisto.debug(f"CTIX fetch_indicators: Enriched {len(enrichment_map)} indicators")

    # Step 4: Map to XSOAR indicator format
    xsoar_indicators: list[dict] = []
    source_reliability = params.get("feedReliability", "")
    for ind in all_indicators_raw:
        xsoar_ind = parse_cyware_indicator(ind, source_reliability=source_reliability, feed_tags=feed_tags, tlp_color=tlp_color)
        if xsoar_ind.get("value"):
            xsoar_indicators.append(xsoar_ind)

    demisto.debug(f"CTIX fetch_indicators: Mapped {len(xsoar_indicators)} indicators")

    if completed_sweep:
        next_run = _store_completed_fetch_state(last_run, state_keys)
    else:
        next_run = _store_partial_fetch_state(last_run, state_keys, next_page_number, from_timestamp)

    return next_run, xsoar_indicators


def _bulk_ioc_lookup_advanced_collect_all_pages(
    client: Client,
    object_type: str,
    values: list[str],
    object_ids: list[str],
    enrichment_data: bool,
    relation_data: bool,
    enrichment_tools: str | None = None,
    fields: str | None = None,
) -> list[dict]:
    """Call bulk IOC lookup advanced and increment page params until exhausted or max pages reached."""
    all_rows: list[dict] = []
    current_page = 1
    while current_page <= BULK_LOOKUP_MAX_PAGES:
        response = execute_with_retry(
            client.bulk_ioc_lookup_advanced,
            object_type=object_type,
            values=values,
            object_ids=object_ids,
            enrichment_data=enrichment_data,
            relation_data=relation_data,
            enrichment_tools=enrichment_tools,
            fields=fields,
            page=current_page,
            page_size=BULK_LOOKUP_PAGE_SIZE,
        )
        if not response:
            break
        data = response.get("data")
        if not isinstance(data, dict):
            break
        rows = data.get("results", [])
        if isinstance(rows, list):
            all_rows.extend(rows)
        next_url = data.get("next")
        if not next_url:
            break
        current_page += 1
    return all_rows


def enrich_indicators_bulk(client: Client, indicators: list[dict]) -> dict[str, dict]:
    """Enrich indicators via bulk IOC lookup advanced.

    Groups indicators by SDO type, batches up to ENRICHMENT_BATCH_SIZE per request,
    and returns a mapping of indicator_id -> enrichment_data.

    :param Client client: CTIX API client
    :param list indicators: List of raw indicator dicts
    :return dict: Mapping of indicator id to enrichment response
    """
    enrichment_map: dict[str, dict] = {}

    # Group by SDO object type
    type_groups: dict[str, list[str]] = {}
    for ind in indicators:
        ind_id = ind.get("id") or ""
        sdo_type = ind.get("sdo_type") or ind.get("type") or "indicator"
        if ind_id:
            type_groups.setdefault(sdo_type, []).append(ind_id)

    for object_type, object_ids in type_groups.items():
        # Batch into chunks of ENRICHMENT_BATCH_SIZE (100)
        for i in range(0, len(object_ids), ENRICHMENT_BATCH_SIZE):
            batch_object_ids = object_ids[i : i + ENRICHMENT_BATCH_SIZE]
            try:
                enriched_list = _bulk_ioc_lookup_advanced_collect_all_pages(
                    client,
                    object_type=object_type,
                    values=[],
                    object_ids=batch_object_ids,
                    enrichment_data=True,
                    relation_data=True,
                    enrichment_tools=None,
                    fields=None,
                )
                for enriched in enriched_list:
                    enriched_id = enriched.get("id") or ""
                    if enriched_id:
                        enrichment_map[enriched_id] = enriched
            except DemistoException as e:
                if RATE_LIMIT_STATUS_ERR in str(e):
                    demisto.error(
                        f"CTIX enrich_indicators_bulk: Rate limit hit again after retry. Returning partial results. Error: {e}"
                    )
                    return enrichment_map
                demisto.debug(f"CTIX enrich_indicators_bulk: Partial failure for batch of {object_type}: {e}")
                continue

    demisto.debug(f"CTIX enrich_indicators_bulk: Enriched {len(enrichment_map)} indicators")
    return enrichment_map


""" COMMAND FUNCTIONS """


def test_module(client: Client):
    """
    Performs basic get request to get sample ip details.
    """
    client.test_auth()
    # test was successful
    demisto.results("ok")


def create_tag_command(client: Client, args: dict[str, str]) -> CommandResults:
    """
    create_tag command: Creates a new tag in the CTIX platform
    """
    name = args["tag_name"]
    color_name = args["color"]

    color_code = tag_colors[color_name]

    response = client.create_tag(name, color_code)
    data = response.get("data")
    data = no_result_found(data)
    if isinstance(data, CommandResults):
        return data
    else:
        results = CommandResults(
            readable_output=tableToMarkdown("Tag Data", data, removeNull=True),
            outputs_prefix="CTIX.Tag",
            outputs_key_field="name",
            outputs=data,
            raw_response=data,
        )
        return results


def get_tags_command(client: Client, args=dict[str, Any]) -> List[CommandResults]:
    """
    get_tags commands: Returns paginated list of tags
    """
    page = args["page"]
    page = check_for_empty_variable(page, 1)
    page_size = args["page_size"]
    page_size = check_for_empty_variable(page_size, 10)
    query = args.get("q", "")
    response = client.get_tags(page, page_size, query)
    response_data = response.get("data", {})
    tags_list = response_data.get("results", [])
    tags_list = no_result_found(tags_list)
    if isinstance(tags_list, CommandResults):
        return [tags_list]
    else:
        results = []
        for tag in tags_list:
            results.append(
                CommandResults(
                    readable_output=tableToMarkdown("Tag Data", tag, removeNull=True),
                    outputs_prefix="CTIX.Tag",
                    outputs_key_field="name",
                    outputs=response_data,
                    raw_response=response,
                )
            )
        return results


def disable_or_enable_tags_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """
    Disable/Enable Tags command

    :Description Disable or Enable tags in CTIX platform
    :param Dict[str, Any] args: Paramters to be send to in request
    :return CommandResults: XSOAR based result
    """
    tag_ids = argToList(args.get("tag_ids"))
    action = args.get("action") or "enabled"
    response = client.disable_or_enable_tag(tag_ids=tag_ids, action=action.lower())
    final_result = response.get("data", {})
    final_result = no_result_found(final_result)
    if isinstance(final_result, CommandResults):
        return final_result
    else:
        results = CommandResults(
            readable_output=tableToMarkdown("Tag Response", [final_result], removeNull=True),
            outputs_prefix="CTIX.TagAction",
            outputs_key_field="result",
            outputs=final_result,
            raw_response=final_result,
        )
        return results


def whitelist_iocs_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """
    Whitelist IOCs command

    :Description Whitelist IOCs for a given value
    :param Dict[str, Any] args: Paramters to be send to in request
    :return CommandResults: XSOAR based result
    """
    ioc_type = args.get("type")
    values = args.get("values")
    values = argToList(values)
    reason = args.get("reason")

    data = client.whitelist_iocs(ioc_type, values, reason).get("data", {}).get("details", {})
    data = no_result_found(data)
    if isinstance(data, CommandResults):
        return data
    else:
        results = CommandResults(
            readable_output=tableToMarkdown("Whitelist IOC", data, removeNull=True),
            outputs_prefix="CTIX.AllowedIOC",
            outputs=data,
            raw_response=data,
        )
        return results


def get_whitelist_iocs_command(client: Client, args=dict[str, Any]) -> List[CommandResults]:
    """
    get_tags commands: Returns paginated list of tags
    """
    page = args["page"]
    page = check_for_empty_variable(page, 1)
    page_size = args["page_size"]
    page_size = check_for_empty_variable(page_size, 10)
    query = args.get("q")
    response = client.get_whitelist_iocs(page, page_size, query)
    response_data = response.get("data", {})
    ioc_list = response_data.get("results", [])
    ioc_list = no_result_found(ioc_list)
    if isinstance(ioc_list, CommandResults):
        return [ioc_list]
    else:
        results = []
        for ioc in ioc_list:
            results.append(
                CommandResults(
                    readable_output=tableToMarkdown("Whitelist IOC", ioc, removeNull=True),
                    outputs_prefix="CTIX.IOC",
                    outputs_key_field="value",
                    outputs=response_data,
                    raw_response=response,
                )
            )
        return results


def remove_whitelisted_ioc_command(client: Client, args=dict[str, Any]) -> CommandResults:
    """
    remove_whitelist_ioc: Deletes a whitelisted ioc with given id
    """
    whitelist_id = argToList(args.get("ids"))
    response = client.remove_whitelisted_ioc(whitelist_id)
    data = response.get("data")
    data = no_result_found(data)
    if isinstance(data, CommandResults):
        return data
    else:
        results = CommandResults(
            readable_output=tableToMarkdown("Details", data, removeNull=True),
            outputs_prefix="CTIX.RemovedIOC",
            outputs_key_field="detail",
            outputs=data,
            raw_response=data,
        )
        return results


def get_threat_data_command(client: Client, args=dict[str, Any]) -> List[CommandResults]:
    """
    get_threat_data: List thread data and allow query
    """
    page = args["page"]
    page = check_for_empty_variable(page, 1)
    page_size = args["page_size"]
    page_size = check_for_empty_variable(page_size, 10)
    query = args.get("query", "type=indicator")
    response = client.get_threat_data(page, page_size, query)
    threat_data_list = response.get("data", {}).get("results", [])
    results = list(threat_data_list)
    results = no_result_found(results)
    reliability = args.get("reliability")

    if isinstance(results, CommandResults):
        return [results]
    else:
        result = iter_dbot_score(
            results,
            "confidence_score",
            "ioc_type",
            "Threat Data",
            "CTIX.ThreatData",
            "id",
            reliability,
        )
        return result


def get_saved_searches_command(client: Client, args=dict[str, Any]) -> CommandResults:
    """
    get_saved_searches: List saved search data
    """
    page = args["page"]
    page = check_for_empty_variable(page, 1)
    page_size = args["page_size"]
    page_size = check_for_empty_variable(page_size, 10)
    response = client.get_saved_searches(page, page_size)
    response_data = response.get("data", {})
    data_list = response_data.get("results", [])
    results = list(data_list)
    results = no_result_found(results)
    if isinstance(results, CommandResults):
        return results
    else:
        result = CommandResults(
            readable_output=tableToMarkdown("Saved Search", results, removeNull=True),
            outputs_prefix="CTIX.SavedSearch",
            outputs_key_field="id",
            outputs=response_data,
            raw_response=response_data,
        )
        return result


def get_server_collections_command(client: Client, args=dict[str, Any]) -> CommandResults:
    """
    get_server_collections: List server collections
    """
    page = args["page"]
    page = check_for_empty_variable(page, 1)
    page_size = args["page_size"]
    page_size = check_for_empty_variable(page_size, 10)
    response = client.get_server_collections(page, page_size)
    response_data = response.get("data", {})
    data_list = response_data.get("results", [])
    results = list(data_list)
    results = no_result_found(results)
    if isinstance(results, CommandResults):
        return results
    else:
        result = CommandResults(
            readable_output=tableToMarkdown("Server Collection", results, removeNull=True),
            outputs_prefix="CTIX.ServerCollection",
            outputs_key_field="id",
            outputs=response_data,
            raw_response=response_data,
        )
        return result


def get_actions_command(client: Client, args=dict[str, Any]) -> CommandResults:
    """
    get_actions: List Actions
    """
    page = args["page"]
    page = check_for_empty_variable(page, 1)
    page_size = args["page_size"]
    page_size = check_for_empty_variable(page_size, 10)
    object_type = args.get("object_type")
    action_type = args.get("actions_type")
    params = {}
    if action_type:
        params["action_type"] = action_type
    if object_type:
        params["object_type"] = object_type
    response = client.get_actions(page, page_size, params)
    response_data = response.get("data", {})
    data_list = response_data.get("results", [])
    results = list(data_list)
    results = no_result_found(results)
    if isinstance(results, CommandResults):
        return results
    else:
        result = CommandResults(
            readable_output=tableToMarkdown("Actions", results, removeNull=True),
            outputs_prefix="CTIX.Action",
            outputs_key_field="id",
            outputs=response_data,
            raw_response=response_data,
        )
        return result


def add_indicator_as_false_positive_command(client: Client, args: dict[str, str]) -> CommandResults:
    """
    Add Indicator as False Positive Command

    :Description Add Indicator as False Positive for a given Indicator
    :param Dict[str, str] args: Paramters to be send to in request
    :return CommandResults: XSOAR based result
    """
    object_ids = args.get("object_ids")
    object_type = args.get("object_type", "indicator")
    object_ids = argToList(object_ids)
    response = client.add_indicator_as_false_positive(object_ids, object_type)
    data = response.get("data")
    data = no_result_found(data)
    if isinstance(data, CommandResults):
        return data
    else:
        results = CommandResults(
            readable_output=tableToMarkdown("Indicator False Positive", data, removeNull=True),
            outputs_prefix="CTIX.IndicatorFalsePositive",
            outputs=data,
            raw_response=data,
        )

        return results


def add_ioc_manual_review_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """
    Add IOC for Manual Review Command

    :Description Add IOC for Manual Review for a given Indicator
    :param Dict[str, str] args: Paramters to be send to in request
    :return CommandResults: XSOAR based result
    """
    object_ids = args.get("object_ids")
    object_type = args.get("object_type", "indicator")
    object_ids = argToList(object_ids)
    response = client.add_ioc_to_manual_review(object_ids, object_type)
    data = response.get("data")
    data = no_result_found(data)
    if isinstance(data, CommandResults):
        return data
    else:
        results = CommandResults(
            readable_output=tableToMarkdown("IOC Manual Review", data, removeNull=True),
            outputs_prefix="CTIX.IOCManualReview",
            outputs=data,
            raw_response=data,
        )

        return results


def deprecate_ioc_command(client: Client, args: dict) -> CommandResults:
    """
    deprecate_ioc command: Deprecate indicators bulk api
    """
    object_ids = args.get("object_ids")
    object_type = args["object_type"]
    object_ids = argToList(object_ids)
    response = client.deprecate_ioc(object_ids, object_type)
    data = response.get("data")
    data = no_result_found(data)
    if isinstance(data, CommandResults):
        return data
    else:
        results = CommandResults(
            readable_output=tableToMarkdown("Deprecate IOC", data, removeNull=True),
            outputs_prefix="CTIX.DeprecateIOC",
            outputs=data,
            raw_response=data,
        )

        return results


def add_analyst_tlp_command(client: Client, args: dict) -> CommandResults:
    """
    Add Analyst TLP Command

    :Description Add Analyst TLP for a given Indicator
    :param Dict[str, str] args: Paramters to be send to in request
    :return CommandResults: XSOAR based result
    """
    object_id = args["object_id"]
    object_type = args["object_type"]
    data = json.loads(args["data"])

    analyst_tlp = data.get("analyst_tlp")
    if not analyst_tlp:
        raise DemistoException("analyst_tlp not provided")

    response = client.add_analyst_tlp(object_id, object_type, data)
    data = response.get("data")
    data = no_result_found(data)
    if isinstance(data, CommandResults):
        return data
    else:
        results = CommandResults(
            readable_output=tableToMarkdown("Add Analyst TLP", data, removeNull=True),
            outputs_prefix="CTIX.AddAnalystTLP",
            outputs=data,
            raw_response=data,
        )

        return results


def add_analyst_score_command(client: Client, args: dict) -> CommandResults:
    """
    Add Analyst Score Command

    :Description Add Analyst Score for a given Indicator
    :param Dict[str, str] args: Paramters to be send to in request
    :return CommandResults: XSOAR based result
    """
    object_id = args["object_id"]
    object_type = args.get("object_type")
    data = json.loads(args.get("data", "{}"))

    analyst_tlp = data.get("analyst_score")
    if not analyst_tlp:
        raise DemistoException("analyst_score not provided")

    response = client.add_analyst_score(object_id, object_type, data)
    data = response.get("data")
    data = no_result_found(data)
    if isinstance(data, CommandResults):
        return data
    else:
        results = CommandResults(
            readable_output=tableToMarkdown("Add Analyst Score", data, removeNull=True),
            outputs_prefix="CTIX.AddAnalystScore",
            outputs=data,
            raw_response=data,
        )
        return results


def saved_result_set_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """
    Get Saved Result Set data Command

    :Description Get Saved Result Set data
    :param Dict[str, str] args: Paramters to be send to in request
    :return CommandResults: XSOAR based result
    """
    page = int(check_for_empty_variable(args["page"], 1))
    page_size = int(check_for_empty_variable(args["page_size"], 10))
    version = args.get("version")
    label_name = args.get("label_name")
    response = client.saved_result_set(page, page_size, label_name, version)
    data = response.get("data", {})
    data_list = data.get("results", [])
    data_list = no_result_found(data_list)

    if isinstance(data_list, CommandResults):
        return data_list
    else:
        results = CommandResults(
            readable_output=tableToMarkdown("Saved Result Set", data_list, removeNull=True),
            outputs_prefix="CTIX.SavedResultSet",
            outputs_key_field="id",
            outputs=data,
            raw_response=data,
        )
        return results


def tag_indicator_updation_command(client: Client, args: dict[str, Any], operation: str) -> CommandResults:
    """
    Tag Indicator Updation Command

    :Description Updating Tag of a given Indicator
    :param Dict[str, str] args: Paramters to be send to in request
    :return CommandResults: XSOAR based result
    """
    page = args.get("page", 1)
    page_size = args.get("page_size", 10)
    object_id = args["object_id"]
    object_type = args["object_type"]
    tag_id = args["tag_id"]
    query = args.get("q", {})

    response = client.tag_indicator_updation(query, page, page_size, object_id, object_type, tag_id, operation)
    data = response.get("data")
    data = no_result_found(data)
    if isinstance(data, CommandResults):
        return data
    else:
        results = CommandResults(
            readable_output=tableToMarkdown("Tag Indicator Updation", data, removeNull=True),
            outputs_prefix="CTIX.TagUpdation",
            outputs=data,
            raw_response=data,
        )

        return results


def search_for_tag_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """
    Search for Tag Command

    :Description Search for Tag
    :param Dict[str, str] args: Paramters to be send to in request
    :return CommandResults: XSOAR based result
    """
    page = args.get("page", 1)
    page_size = args.get("page_size", 10)
    q = args.get("q")
    params = {"page": page, "page_size": page_size, "q": q}

    response = client.search_for_tag(params)
    response_data = response.get("data", {})
    data = response_data.get("results", [])
    data = no_result_found(data)
    if isinstance(data, CommandResults):
        return data
    else:
        results = CommandResults(
            readable_output=tableToMarkdown("Search for Tag", data, removeNull=True),
            outputs_prefix="CTIX.SearchTag",
            outputs=response_data,
            raw_response=response_data,
        )

        return results


def get_indicator_details_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """
    Get Indicator Details Command

    :Description Get Indicator Details
    :param Dict[str, str] args: Paramters to be send to in request
    :return CommandResults: XSOAR based result
    """
    page = args.get("page", 1)
    page_size = args.get("page_size", 10)
    object_id = args["object_id"]
    object_type = args["object_type"]
    params = {"page": page, "page_size": page_size}

    response = client.get_indicator_details(object_type.lower(), object_id, params)
    data = response.get("data")
    data = no_result_found(data)
    if isinstance(data, CommandResults):
        return data
    else:
        results = CommandResults(
            readable_output=tableToMarkdown("Get Indicator Details", data, removeNull=True),
            outputs_prefix="CTIX.IndicatorDetails",
            outputs=data,
            raw_response=data,
        )
        return results


def get_indicator_tags_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """
    Get Indicator Tags  Command

    :Description Get Tags Details
    :param Dict[str, str] args: Paramters to be send to in request
    :return CommandResults: XSOAR based result
    """
    page = args.get("page", 1)
    page_size = args.get("page_size", 10)
    object_id = args["object_id"]
    object_type = args["object_type"]
    params = {"page": page, "page_size": page_size}

    response = client.get_indicator_tags(object_type, object_id, params)
    data = response.get("data", {})
    data = no_result_found(data)
    if isinstance(data, CommandResults):
        return data
    else:
        results = CommandResults(
            readable_output=tableToMarkdown("Get Indicator Tags", data, removeNull=True),
            outputs_prefix="CTIX.IndicatorTags",
            outputs=data,
            raw_response=data,
        )

        return results


def get_indicator_relations_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """
    Get Indicator Relations Command

    :Description Get Relations Details
    :param Dict[str, str] args: Paramters to be send to in request
    :return CommandResults: XSOAR based result
    """
    page = args.get("page", 1)
    page_size = args.get("page_size", 10)
    object_id = args["object_id"]
    object_type = args["object_type"]
    params = {"page": page, "page_size": page_size}
    response = client.get_indicator_relations(object_type, object_id, params)
    response_data = response.get("data", {})
    data = response_data.get("results", {})
    data = no_result_found(data)
    if isinstance(data, CommandResults):
        return data
    else:
        results = CommandResults(
            readable_output=tableToMarkdown("Get Object Relations", data, removeNull=True),
            outputs_prefix="CTIX.IndicatorRelations",
            outputs=response_data,
            raw_response=response_data,
        )

        return results


def get_indicator_observations_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """
    Get Indicator Observations Command

    :Description Get Indicator Observations
    :param Dict[str, str] args: Paramters to be send to in request
    :return CommandResults: XSOAR based result
    """
    page = args.get("page", 1)
    page_size = args.get("page_size", 10)
    object_id = args.get("object_id")
    object_type = args.get("object_type")
    params = {
        "page": page,
        "page_size": page_size,
        "object_id": object_id,
        "object_type": object_type,
    }

    response = client.get_indicator_observations(params)
    response_data = response.get("data", {})
    data = response_data.get("results", {})
    data = no_result_found(data)
    if isinstance(data, CommandResults):
        return data
    else:
        results = CommandResults(
            readable_output=tableToMarkdown("Get Indicator Observations", data, removeNull=True),
            outputs_prefix="CTIX.IndicatorObservations",
            outputs=response_data,
            raw_response=response_data,
        )

        return results


def get_conversion_feed_source_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """
    Get Conversion Feed Source Command

    :Description Get Conversion Feed Source
    :param Dict[str, str] args: Paramters to be send to in request
    :return CommandResults: XSOAR based result
    """
    page = args.get("page", 1)
    page_size = args.get("page_size", 10)
    object_id = args.get("object_id")
    object_type = args.get("object_type")
    params = {
        "page": page,
        "page_size": page_size,
        "object_id": object_id,
        "object_type": object_type,
    }
    q = args.get("q")
    if q is not None:
        params.update({"q": q})

    response = client.get_conversion_feed_source(params)
    response_data = response.get("data", {})
    data = response_data.get("results", {})
    data = no_result_found(data)
    if isinstance(data, CommandResults):
        return data
    else:
        results = CommandResults(
            readable_output=tableToMarkdown("Conversion Feed Source", data, removeNull=True),
            outputs_prefix="CTIX.ConversionFeedSource",
            outputs=response_data,
            raw_response=response_data,
        )

        return results


def get_lookup_threat_data_command(client: Client, args: dict[str, Any]) -> List[CommandResults]:
    """
    Get Lookup Threat Data Command
    :Description Get Lookup Threat Data
    :param Dict[str, str] args: Paramters to be send to in request
    :return CommandResults: XSOAR based result
    """
    object_type = args.get("object_type", "indicator")
    ioc_type = argToList(args.get("ioc_type"))
    object_names = argToList(args.get("object_names"))
    page_size = args.get("page_size", 10)
    params = {"page_size": page_size}
    response = client.get_lookup_threat_data(object_type, ioc_type, object_names, params)
    data_set = response.get("data").get("results")
    results = no_result_found(data_set)
    reliability = args.get("reliability")

    if isinstance(results, CommandResults):
        return [results]
    else:
        results = iter_dbot_score(
            results, "confidence_score", "ioc_type", "Lookup Data", "CTIX.ThreatDataLookup", "id", reliability
        )
        return results


def get_create_threat_data_command(client: Client, args: dict[str, Any]) -> List[CommandResults]:
    """
    Get or Create Threat Data Command

    :Description Gets Threat Data or creates it if it does not exist
    :param Dict[str, str] args: Paramters to be send to in request
    :return CommandResults: XSOAR based result
    """
    object_names = argToList(args.get("object_names"))
    source = args.get("source", "XSOAR")
    collection = args.get("collection", "Intel")
    page_size = args.get("page_size", 10)
    reliability = args.get("reliability")
    created_after_lookup_results = []
    invalid_values_results = []

    response = client.bulk_lookup_and_create_data(object_names, source, collection, page_size).get("data", {})
    results = response.get("found_iocs", {}).get("results", [])
    created_after_lookup = response["values_not_found"]["valid_iocs"]
    invalid_values = response["values_not_found"]["invalid_values"]

    if created_after_lookup:
        created_after_lookup_results.append(
            CommandResults(
                readable_output=tableToMarkdown("Not Found: Created", created_after_lookup, headers=["Name"], removeNull=True),
                outputs_prefix="CTIX.ThreatDataGetCreate.NotFoundCreated",
                outputs=created_after_lookup,
            )
        )

    if invalid_values:
        invalid_values_results.append(
            [
                CommandResults(
                    readable_output=tableToMarkdown("Not Found: Invalid", invalid_values, headers=["Name"], removeNull=True),
                    outputs_prefix="CTIX.ThreatDataGetCreate.NotFoundInvalid",
                    outputs=invalid_values,
                )
            ]
        )

    if isinstance(results, CommandResults):
        return [results]
    else:
        results = iter_dbot_score(
            results,
            "confidence_score",
            "ioc_type",
            "Lookup Data",
            "CTIX.ThreatDataGetCreate.Found",
            "id",
            reliability,
        )

        return results + created_after_lookup_results + invalid_values_results


def domain(client: Client, args: dict[str, Any]) -> List[CommandResults]:
    args["object_names"] = args["domain"]
    args["ioc_type"] = ["domain-name"]
    return get_lookup_threat_data_command(client, args)


def bulk_ioc_lookup_advanced_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """
    Bulk IOC Lookup Advanced command
    """
    object_type: str = args.get("object_type") or ""
    values = argToList(args.get("values"))
    object_ids = argToList(args.get("object_ids"))
    enrichment_data: bool = argToBoolean(args.get("enrichment_data", "true"))
    relation_data: bool = argToBoolean(args.get("relation_data", "false"))
    enrichment_tools: str | None = args.get("enrichment_tools") or None
    fields: str | None = args.get("fields") or None
    page_size = arg_to_number(args.get("page_size", 10))
    page = arg_to_number(args.get("page", 1))

    response = client.bulk_ioc_lookup_advanced(
        object_type=object_type,
        values=values,
        object_ids=object_ids,
        enrichment_data=enrichment_data,
        relation_data=relation_data,
        enrichment_tools=enrichment_tools,
        fields=fields,
        page_size=page_size,
        page=page,
    )

    response_data = response.get("data")
    result = response_data.get("results", []) if isinstance(response_data, dict) else []
    checked = no_result_found(result)

    if isinstance(checked, CommandResults):
        return checked
    else:
        return CommandResults(
            readable_output=tableToMarkdown("Bulk IOC Lookup (Advanced)", result, removeNull=True),
            outputs_prefix="CTIX.BulkIOCLookupAdvanced",
            outputs=response_data,
            raw_response=response,
        )


def url(client: Client, args: dict[str, Any]) -> List[CommandResults]:
    args["object_names"] = args["url"]
    args["ioc_type"] = ["url"]
    return get_lookup_threat_data_command(client, args)


def ip(client: Client, args: dict[str, Any]) -> List[CommandResults]:
    args["object_names"] = args["ip"]
    args["ioc_type"] = ["ipv4-addr", "ipv6-addr"]
    return get_lookup_threat_data_command(client, args)


def file(client: Client, args: dict[str, Any]) -> List[CommandResults]:
    args["object_names"] = args["file"]
    args["ioc_type"] = [
        "MD5",
        "SHA-1",
        "SHA-224",
        "SHA-256",
        "SHA-384",
        "SHA-512",
        "SSDEEP",
    ]
    return get_lookup_threat_data_command(client, args)


def get_all_notes(client: Client, args: dict[str, Any]) -> CommandResults:
    page = args["page"]
    page = check_for_empty_variable(page, 1)
    page_size = args["page_size"]
    page_size = check_for_empty_variable(page_size, 10)
    response = {}
    object_id = args.get("object_id", "")
    params = {
        "page": page,
        "page_size": page_size,
    }

    if object_id:
        if not is_valid_uuid(object_id):
            return_error("Error: The object ID that was provided is not valid.")
        else:
            params["object_id"] = object_id

    client_url = client.base_url + "ingestion/notes/"
    response = client.get_http_request(client_url, **params)
    response_data = response.get("data", {})
    notes_list = response_data.get("results", [])
    notes_list = no_result_found(notes_list)

    if isinstance(notes_list, CommandResults):
        return notes_list
    else:
        return CommandResults(
            readable_output=tableToMarkdown("Note Data", notes_list, removeNull=True),
            outputs_prefix="CTIX.Note",
            outputs_key_field="id",
            outputs=response_data,
            raw_response=response_data,
        )


def get_note_details(client: Client, args: dict[str, Any]) -> CommandResults:
    id = args["id"]
    client_url = client.base_url + f"ingestion/notes/{id}/"
    response = client.get_http_request(client_url)

    if response["status"] == HTTPStatus.BAD_REQUEST:
        return_error("Error: Note details could not be retrieved because a note with the provided ID could not be found.")

    note_detail = response.get("data", {})
    note_detail = no_result_found(note_detail)

    if isinstance(note_detail, CommandResults):
        return note_detail
    else:
        return CommandResults(
            readable_output=tableToMarkdown("Note Detail Data", note_detail, removeNull=True),
            outputs_prefix="CTIX.Note",
            outputs_key_field="id",
            outputs=note_detail,
        )


def create_note(client: Client, args: dict[str, Any]) -> CommandResults:
    text = args["text"]
    client_url = client.base_url + "ingestion/notes/"
    object_id = args.get("object_id", None)
    object_id = check_for_empty_variable(object_id, None)
    object_type = args.get("object_type", None)
    object_type = check_for_empty_variable(object_type, None)

    if object_id and not is_valid_uuid(object_id):
        return_error("Error: The `object_id` that was provided is not valid.")
    elif object_id and not object_type:
        return_error("Error: `object_type` must be set as well if `object_id` is provided.")
    elif object_type and not object_id:
        return_error("Error: `object_id` must be set as well if `object_type` is provided.")

    payload = {"text": text, "type": "notes", "meta_data": {"component": "notes"}}

    if object_id:
        payload["meta_data"]["component"] = "threatdata"
        payload["meta_data"]["object_id"] = object_id
        payload["meta_data"]["type"] = object_type
        payload["object_id"] = object_id
        payload["type"] = "threatdata"

    response = client.post_http_request(client_url, payload=payload, params={})
    resp = response.get("data", {})
    resp = no_result_found(resp)

    if isinstance(resp, CommandResults):
        return resp
    else:
        return CommandResults(
            readable_output=tableToMarkdown("Created Note Data", resp, removeNull=True),
            outputs_prefix="CTIX.Note",
            outputs_key_field="id",
            outputs=resp,
        )


def update_note(client: Client, args: dict[str, Any]) -> CommandResults:
    id = args["id"]
    text = args.get("text", None)
    client_url = client.base_url + f"ingestion/notes/{id}/"
    object_id = args.get("object_id", None)
    object_id = check_for_empty_variable(object_id, None)
    object_type = args.get("object_type", None)
    object_type = check_for_empty_variable(object_type, None)

    if object_id and not is_valid_uuid(object_id):
        return_error("Error: The `object_id` that was provided is not valid.")
    elif object_id and not object_type:
        return_error("Error: `object_type` must be set as well if `object_id` is provided.")
    elif object_type and not object_id:
        return_error("Error: `object_id` must be set as well if `object_type` is provided.")

    payload = {}

    if text:
        payload["text"] = text

    if object_id:
        payload["meta_data"] = {}
        payload["meta_data"]["component"] = "threatdata"
        payload["meta_data"]["object_id"] = object_id
        payload["meta_data"]["type"] = object_type
        payload["object_id"] = object_id
        payload["type"] = "threatdata"

    if not payload:
        return CommandResults(readable_output="Finished processing. No values to update.")

    response = client.put_http_request(client_url, payload=payload, params={})

    if response["status"] == HTTPStatus.BAD_REQUEST:
        return_error("Error: The note could not be updated because a note with the provided ID could not be found.")

    resp = response.get("data", {})
    resp = no_result_found(resp)

    if isinstance(resp, CommandResults):
        return resp
    else:
        return CommandResults(
            readable_output=tableToMarkdown("Updated Note Data", resp, removeNull=True),
            outputs_prefix="CTIX.Note",
            outputs_key_field="id",
            outputs=resp,
        )


def delete_note(client: Client, args: dict[str, Any]) -> CommandResults:
    id = args["id"]
    client_url = client.base_url + f"ingestion/notes/{id}/"
    response = client.delete_http_request(client_url)

    if response["status"] == HTTPStatus.BAD_REQUEST:
        return_error("Error: The note could not be deleted because a note with the provided ID could not be found.")

    resp = response.get("data", {})
    resp = no_result_found(resp)

    if isinstance(resp, CommandResults):
        return resp
    else:
        return CommandResults(
            readable_output=tableToMarkdown("Deleted Note Data", resp, removeNull=True),
            outputs_prefix="CTIX.Note",
            outputs_key_field="details",
            outputs=resp,
        )


def make_request(client: Client, args: dict[str, Any]) -> List[CommandResults]:
    type = args["type"]
    body = json.loads(args.get("body", "{}"))
    params = json.loads(args.get("params", "{}"))
    client_url = client.base_url + args["endpoint"].lstrip("/")
    response = {}

    if type == "GET":
        response = client.get_http_request(client_url, body, params=params)
    elif type == "POST":
        response = client.post_http_request(client_url, body, params=params)
    elif type == "PUT":
        response = client.put_http_request(client_url, body, params=params)
    elif type == "DELETE":
        response = client.delete_http_request(client_url, body, params=params)

    resp = response.get("data", {})

    if "results" in resp:
        resp = resp["results"]

    resp = no_result_found(resp)

    if isinstance(resp, CommandResults):
        return [resp]
    else:
        if isinstance(resp, list):
            results = []
            for item in resp:
                results.append(
                    CommandResults(
                        readable_output=tableToMarkdown("HTTP Response Data", item, removeNull=True),
                        outputs_prefix=f"CTIX.Request.{type}.{args['endpoint']}",
                        outputs=item,
                    )
                )
            return results
        else:
            return [
                CommandResults(
                    readable_output=tableToMarkdown("HTTP Response Data", resp, removeNull=True),
                    outputs_prefix=f"CTIX.Request.{type}.{args['endpoint']}",
                    outputs=resp,
                )
            ]


def cve_command(client: Client, args: dict[str, Any]) -> List[CommandResults]:
    page = 1
    page_size = 15
    params = {"page": page, "page_size": page_size}
    cve = argToList(args["cve"])
    extra_fields = argToList(args.get("extra_fields", []))
    response = client.get_lookup_threat_data("vulnerability", [], cve, params)
    threat_data_list = response.get("data", {}).get("results", [])
    results = list(threat_data_list)
    results = no_result_found(results)

    if isinstance(results, CommandResults):
        return [results]

    final_results = []
    for result in results:
        final_results.append(_lookup_cve_result(client, result, page, page_size, extra_fields))
    return final_results


def _lookup_cve_result(client: Client, cve_detail: dict[str, Any], page: int, page_size: int, extra_fields: List[str]):
    cve_uuid = str(cve_detail.get("id"))
    created = str(datetime.fromtimestamp(cve_detail.get("created", 0)))
    modified = str(datetime.fromtimestamp(cve_detail.get("modified", 0)))
    name = cve_detail.get("name")
    extra_field_values = {k: cve_detail.get(k, None) for k in extra_fields}
    cve_sources = [source.get("id") for source in cve_detail.get("sources", []) if source.get("id")]

    response = client.get_vulnerability_product_details(cve_uuid, page, page_size)
    product_details_list = response.get("data", {}).get("results", [])
    results = list(product_details_list)
    cpe_list = ",\n".join(product.get("product") for product in results)

    response = client.get_vulnerability_cvss_score(cve_uuid, page, page_size)
    cvss_score_list = response.get("data", {}).get("results", [])
    cvss2 = next((result.get("cssv2") for result in cvss_score_list if result.get("cssv2")), None)
    cvss3 = next((result.get("cssv3") for result in cvss_score_list if result.get("cssv3")), None)
    cvss_map_value = 0
    if cvss3:
        cvss_map_value = cvss3
    elif cvss2:
        cvss_map_value = cvss2

    dbot_reputation_score = 0
    if 0 < cvss_map_value < 3:
        dbot_reputation_score = 1
    elif 3 <= cvss_map_value < 7:
        dbot_reputation_score = 2
    elif cvss_map_value >= 7:
        dbot_reputation_score = 3

    description = None
    if cve_sources:
        for source in cve_sources:
            response = client.get_vulnerability_source_description(cve_uuid, source, page, page_size)
            source_description = response.get("data", {}).get("result", {})
            if source_description:
                description = source_description.get("description")
                if description:
                    break

    cve_standard_context = Common.CVE(name, cvss2, created, modified, description)
    data = {
        "cpes": cpe_list or "None",
        "cvss2": cvss2 or "None",
        "cvss3": cvss3 or "None",
        "dbot_reputation": dbot_reputation_score,
        "description": description,
        "last_modified": modified,
        "last_published": created,
        "name": name,
        "uuid": cve_uuid,
        "extra_data": json.dumps(extra_field_values),
    }
    return CommandResults(
        readable_output=tableToMarkdown("Get CVE Information", data, removeNull=True),
        outputs_prefix="CTIX.VulnerabilityLookup",
        outputs=data,
        outputs_key_field="id",
        indicator=cve_standard_context,
        raw_response=data,
    )


def main() -> None:
    params = demisto.params()
    args = demisto.args()
    base_url = params.get("base_url")
    access_id = params.get("access_id")
    secret_key = params.get("secret_key")
    verify = not params.get("insecure", False)
    timeout = arg_to_number(params.get("timeout")) or 180
    reliability = params.get("integrationReliability", DBotScoreReliability.C)

    if DBotScoreReliability.is_valid_type(reliability):
        reliability = DBotScoreReliability.get_dbot_score_reliability_from_str(reliability)
    else:
        raise Exception("Please provide a valid value for the Source Reliability parameter.")

    args["reliability"] = reliability
    proxies = handle_proxy(proxy_param_name="proxy")
    demisto.debug(f"Command being called is {demisto.command()}")

    try:
        client = Client(
            base_url=base_url, access_id=access_id, secret_key=secret_key, verify=verify, proxies=proxies, timeout=timeout
        )

        command = demisto.command()

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

        elif command == "fetch-incidents":
            last_run = demisto.getLastRun()
            next_run, incidents = fetch_incidents(client, params, last_run)
            demisto.setLastRun(next_run)
            demisto.incidents(incidents)

        elif command == "fetch-indicators":
            last_run = demisto.getLastRun()
            next_run, indicators = fetch_indicators(client, params, last_run)
            # Submit indicators in manageable batches to reduce command payload size.
            for iter_ in batch(indicators, batch_size=1000):
                demisto.createIndicators(iter_)
            demisto.setLastRun(next_run)

        else:
            CMD_TO_FUNC = {
                "ctix-create-tag": (create_tag_command, (client, args)),
                "ctix-get-tags": (get_tags_command, (client, args)),
                "ctix-disable-or-enable-tags": (disable_or_enable_tags_command, (client, args)),
                "ctix-allowed-iocs": (whitelist_iocs_command, (client, args)),
                "ctix-get-allowed-iocs": (get_whitelist_iocs_command, (client, args)),
                "ctix-remove-allowed-ioc": (remove_whitelisted_ioc_command, (client, args)),
                "ctix-get-threat-data": (get_threat_data_command, (client, args)),
                "ctix-get-saved-searches": (get_saved_searches_command, (client, args)),
                "ctix-get-server-collections": (get_server_collections_command, (client, args)),
                "ctix-get-actions": (get_actions_command, (client, args)),
                "ctix-ioc-manual-review": (add_ioc_manual_review_command, (client, args)),
                "ctix-deprecate-ioc": (deprecate_ioc_command, (client, args)),
                "ctix-add-analyst-tlp": (add_analyst_tlp_command, (client, args)),
                "ctix-add-analyst-score": (add_analyst_score_command, (client, args)),
                "ctix-saved-result-set": (saved_result_set_command, (client, args)),
                "ctix-add-tag-indicator": (tag_indicator_updation_command, (client, args, "add_tag_indicator")),
                "ctix-remove-tag-from-indicator": (tag_indicator_updation_command, (client, args, "remove_tag_from_indicator")),
                "ctix-search-for-tag": (search_for_tag_command, (client, args)),
                "ctix-get-indicator-details": (get_indicator_details_command, (client, args)),
                "ctix-get-indicator-tags": (get_indicator_tags_command, (client, args)),
                "ctix-get-object-relations": (get_indicator_relations_command, (client, args)),
                "ctix-get-indicator-observations": (get_indicator_observations_command, (client, args)),
                "ctix-get-conversion-feed-source": (get_conversion_feed_source_command, (client, args)),
                "ctix-get-lookup-threat-data": (get_lookup_threat_data_command, (client, args)),
                "ctix-bulk-ioc-lookup-advanced": (bulk_ioc_lookup_advanced_command, (client, args)),
                "ctix-get-create-threat-data": (get_create_threat_data_command, (client, args)),
                "ctix-add-indicator-as-false-positive": (add_indicator_as_false_positive_command, (client, args)),
                "domain": (domain, (client, args)),
                "url": (url, (client, args)),
                "ip": (ip, (client, args)),
                "file": (file, (client, args)),
                "cve": (cve_command, (client, args)),
                "ctix-get-all-notes": (get_all_notes, (client, args)),
                "ctix-get-note-details": (get_note_details, (client, args)),
                "ctix-create-note": (create_note, (client, args)),
                "ctix-update-note": (update_note, (client, args)),
                "ctix-delete-note": (delete_note, (client, args)),
                "ctix-make-request": (make_request, (client, args)),
                "ctix-get-vulnerability-data": (cve_command, (client, args)),
            }

            func, cmd_args = CMD_TO_FUNC[command]
            return_results(cast(Callable, func)(*cmd_args))

    except Exception as e:
        demisto.error(traceback.format_exc())  # print the traceback
        return_error(
            f"Failed to execute {demisto.command()} command.\nError:\n{e!s} \
            {traceback.format_exc()}"
        )


if __name__ in ("__main__", "__builtin__", "builtins"):
    main()

# register_module_line("CTIX v3", "end", __line__())