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
| ID | CTIX v3 |
|---|---|
| Provider | Cyware Labs |
| Category | Data Enrichment & Threat Intelligence |
| From Version | 6.0.0 |
| Docker Image | demisto/python3:3.12.13.10404775 |
| Supported Modules | Agentix 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 settingstimeout— TimeoutisFetch— Fetch incidentsincidentFetchInterval— Incidents Fetch IntervalincidentType— Incident typeclassifier— Classifiermapper— Mapper (incoming)integrationReliability— Source Reliabilityfirst_fetch— First fetch timemax_fetch— Maximum number of incidents per fetchincident_fetch_query— Incident Fetch CQL Queryfeed— Fetch indicatorssaved_result_set_label— Saved Result Set Labelsaved_result_set_version— Saved Result Set Versionretrieve_enriched_data— Retrieve Enriched DatafeedFetchInterval— Feed Fetch IntervalfeedReliability— Source Reliability (required)feedReputation— Indicator ReputationfeedExpirationPolicy—feedExpirationInterval—tlp_color— Traffic Light Protocol ColorfeedBypassExclusionList— Bypass exclusion listfeedTags— Tags
Commands (39)
-
ctix-add-analyst-scoreAdd Analyst Score for a Threat data.
-
ctix-add-analyst-tlpAdd Analyst TLP.
-
ctix-add-indicator-as-false-positiveAdd indicators as false positive in CTIX.
-
ctix-add-tag-indicatorAdding Tag to Indicator.
-
ctix-allowed-iocsAdds list of same type of iocs to allowed.
-
ctix-bulk-ioc-lookup-advancedPerforms a bulk lookup for threat data objects in the CTIX platform and retrieves details such as basic info, enriched data, and relations.
-
ctix-create-noteCreates a new Note from the parameter 'text'.
-
ctix-create-tagCreate new tag in the ctix platform.
-
ctix-delete-noteDeletes an existing Note, as specified by its ID.
-
ctix-deprecate-iocDeprecate ioc bulk api.
-
ctix-disable-or-enable-tagsDisables or enables one or more tags based on their IDs.
-
ctix-get-actionsEnrichment tools listing API.
-
ctix-get-all-notesGet paginated list of Notes.
-
ctix-get-allowed-iocsget paginated list of allowed iocs.
-
ctix-get-conversion-feed-sourceGet Conversion feed source.
-
ctix-get-create-threat-dataGets or creates threat data.
-
ctix-get-indicator-detailsGet Indicator Details.
-
ctix-get-indicator-observationsGet Indicator Observations.
-
ctix-get-indicator-tagsGet Indicator Tags.
-
ctix-get-lookup-threat-dataLookup to get threat data.
-
ctix-get-note-detailsGet details of a Note as specified by its ID.
-
ctix-get-object-relationsGet Object Relations.
-
ctix-get-saved-searchesSaved Search listing api with pagination.
-
ctix-get-server-collectionsSource Collection listing api with pagination.
-
ctix-get-tagsGet paginated list of tags.
-
ctix-get-threat-dataCommand for querying and listing threat data.
-
ctix-get-vulnerability-dataLookup vulnerability info.
-
ctix-ioc-manual-reviewAdds ioc to manual review bulk api.
-
ctix-make-requestallows you to make any HTTP request using CTIX endpoints.
-
ctix-remove-allowed-iocRemoves a allowed ioc with given id.
-
ctix-remove-tag-from-indicatorRemove Tag From Indicator.
-
ctix-saved-result-setSaved Result Set.
-
ctix-search-for-tagSearch for tag.
-
ctix-update-noteUpdates the Note text from an existing Note, as specified by its ID.
-
cveLookup vulnerability info.
-
domainLookup domain threat data.
-
fileLookup file threat data.
-
ipLookup ip threat data.
-
urlLookup url threat data.
import json import re import pytest from unittest.mock import patch, MagicMock from datetime import datetime, UTC from urllib.parse import parse_qs, urlparse from CTIXv3 import ( Client, DemistoException, add_analyst_score_command, add_analyst_tlp_command, add_indicator_as_false_positive_command, add_ioc_manual_review_command, bulk_ioc_lookup_advanced_command, confidence_to_dbot_score, create_note, create_tag_command, cve_command, delete_note, disable_or_enable_tags_command, deprecate_ioc_command, domain, enrich_indicators_bulk, execute_with_retry, fetch_incidents, fetch_indicators, file, get_actions_command, get_all_notes, get_conversion_feed_source_command, get_indicator_details_command, get_indicator_observations_command, get_indicator_relations_command, get_indicator_tags_command, get_lookup_threat_data_command, get_note_details, get_saved_searches_command, get_server_collections_command, get_tags_command, get_threat_data_command, get_whitelist_iocs_command, ip, make_request, map_ctix_indicator_to_xsoar, map_report_severity, map_report_to_incident, normalize_indicator_type, remove_whitelisted_ioc_command, saved_result_set_command, search_for_tag_command, tag_indicator_updation_command, update_note, url, whitelist_iocs_command, ) """CONSTANTS""" BASE_URL = "http://test.com/" ACCESS_ID = "access_id" SECRET_KEY = "secret_key" def util_load_json(path): with open(path, encoding="utf-8") as f: return json.loads(f.read()) def _register_fetch_incidents_bulk_mock(requests_mock): """Mock bulk IOC lookup (report) used by fetch_incidents for relations.""" bulk = util_load_json("test_data/fetch_incidents_bulk_lookup.json") requests_mock.post(re.compile(re.escape(BASE_URL) + r"ingestion/openapi/bulk-lookup/report/"), json=bulk) def test_create_tag(requests_mock): mock_response = util_load_json("test_data/create_tag.json") requests_mock.post(f"{BASE_URL}ingestion/tags/", json=mock_response) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) args = {"tag_name": "demisto_test_temp", "color": "blue"} response = create_tag_command(client, args) assert response.outputs == mock_response assert response.outputs_prefix == "CTIX.Tag" assert response.outputs_key_field == "name" assert isinstance(response.raw_response, dict) assert len(response.raw_response) == 8 def test_create_tag_command_already_exists(requests_mock): requests_mock.post(f"{BASE_URL}ingestion/tags/", json=[]) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) args = {"tag_name": "demisto_test_temp", "color": "blue"} response = create_tag_command(client, args) assert response.outputs is None def test_get_tags(requests_mock): mock_response = util_load_json("test_data/get_tags.json") requests_mock.get(f"{BASE_URL}ingestion/tags/", json=mock_response) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) args = {"page": 1, "page_size": 1} response = get_tags_command(client, args) # the command returns the full paginated response; compare against it assert response[0].outputs == mock_response assert response[0].outputs_prefix == "CTIX.Tag" assert response[0].outputs_key_field == "name" assert isinstance(response, list) assert len(response) == 1 def test_get_tags_not_found(requests_mock): requests_mock.get(f"{BASE_URL}ingestion/tags/", json={}) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) args = {"page": 1, "page_size": 1} response = get_tags_command(client, args) assert response[0].outputs is None def test_disable_or_enable_tag(requests_mock): mock_response = util_load_json("test_data/disable_or_enable_tag.json") requests_mock.post(f"{BASE_URL}ingestion/tags/bulk-actions/", json=mock_response) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) args = {"tag_id": "foo, bar", "action": "disabled"} response = disable_or_enable_tags_command(client, args) assert response.outputs == mock_response assert response.outputs_prefix == "CTIX.TagAction" assert response.outputs_key_field == "result" assert isinstance(response.raw_response, dict) def test_disable_or_enable_tag_no_inputs(requests_mock): requests_mock.post(f"{BASE_URL}ingestion/tags/bulk-actions/", json={}) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) args = {"tag_id": "", "action": "disabled"} response = disable_or_enable_tags_command(client, args) assert response.outputs is None def test_whitelist_iocs_command(requests_mock): mock_response = util_load_json("test_data/whitelist_iocs.json") requests_mock.post(f"{BASE_URL}conversion/allowed_indicators/", json=mock_response) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) args = {"type": "indicator", "values": "127.0.0.1, 127.0.0.2", "reason": "test"} resp = whitelist_iocs_command(client, args) response = resp.raw_response assert response == mock_response["details"] assert resp.outputs_prefix == "CTIX.AllowedIOC" assert isinstance(response, dict) assert len(response) == 3 def test_whitelist_iocs_command_fallback(requests_mock): mock_response = util_load_json("test_data/whitelist_iocs.json") requests_mock.post(f"{BASE_URL}conversion/allowed_indicators/", status_code=404) requests_mock.post(f"{BASE_URL}conversion/whitelist/", json=mock_response) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) args = {"type": "indicator", "values": "127.0.0.1, 127.0.0.2", "reason": "test"} resp = whitelist_iocs_command(client, args) response = resp.raw_response assert response == mock_response["details"] assert resp.outputs_prefix == "CTIX.AllowedIOC" assert isinstance(response, dict) assert len(response) == 3 def test_get_whitelist_iocs_command_fallback(requests_mock): mock_response = util_load_json("test_data/get_whitelist_iocs.json") requests_mock.get(f"{BASE_URL}conversion/allowed_indicators/", status_code=404) requests_mock.get(f"{BASE_URL}conversion/whitelist/", json=mock_response) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) args = {"page": 1, "page_size": 1} resp = get_whitelist_iocs_command(client, args) # client command returns a wrapper with 'data' and 'status' response = resp[0].raw_response.get("data") if isinstance(resp[0].raw_response, dict) else resp[0].raw_response # the command returns the paginated data under the 'data' key; compare against it assert response == mock_response assert resp[0].outputs_prefix == "CTIX.IOC" assert isinstance(response, dict) assert len(response) == 5 def test_get_whitelist_iocs_command(requests_mock): mock_response = util_load_json("test_data/get_whitelist_iocs.json") requests_mock.get(f"{BASE_URL}conversion/allowed_indicators/", json=mock_response) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) args = {"page": 1, "page_size": 1} resp = get_whitelist_iocs_command(client, args) # client command returns a wrapper with 'data' and 'status' response = resp[0].raw_response.get("data") if isinstance(resp[0].raw_response, dict) else resp[0].raw_response # the command returns the paginated data under the 'data' key; compare against it assert response == mock_response assert resp[0].outputs_prefix == "CTIX.IOC" assert isinstance(response, dict) assert len(response) == 5 def test_remove_whitelisted_ioc_command(requests_mock): mock_id = "foo" mock_response = util_load_json("test_data/remove_whitelist_ioc.json") requests_mock.post(f"{BASE_URL}conversion/allowed_indicators/bulk-actions/", json=mock_response) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) args = {"ids": mock_id} response = remove_whitelisted_ioc_command(client, args) assert response.outputs == mock_response assert response.outputs_prefix == "CTIX.RemovedIOC" assert isinstance(response.raw_response, dict) assert len(response.raw_response) == 1 def test_get_threat_data_command(requests_mock): mock_response = util_load_json("test_data/get_threat_data.json") requests_mock.post(f"{BASE_URL}ingestion/threat-data/list/", json=mock_response) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) args = { "page": 1, "page_size": 1, } response = get_threat_data_command(client, args) assert response[0].outputs == mock_response["results"][0] assert response[0].outputs_prefix == "CTIX.ThreatData" assert isinstance(response[0].outputs, dict) assert len(response[0].outputs) == 37 def test_get_saved_searches_command(requests_mock): mock_response = util_load_json("test_data/get_threat_data.json") requests_mock.get(f"{BASE_URL}ingestion/saved-searches/", json=mock_response) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) args = { "page": 1, "page_size": 1, } response = get_saved_searches_command(client, args) assert response.outputs == mock_response assert response.outputs_prefix == "CTIX.SavedSearch" assert isinstance(response.raw_response, dict) def test_get_server_collections_command(requests_mock): mock_response = util_load_json("test_data/get_threat_data.json") requests_mock.get(f"{BASE_URL}publishing/collection/", json=mock_response) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) args = { "page": 1, "page_size": 1, } response = get_server_collections_command(client, args) assert response.outputs == mock_response assert response.outputs_prefix == "CTIX.ServerCollection" assert isinstance(response.raw_response, dict) def test_get_actions_command(requests_mock): mock_response = util_load_json("test_data/get_actions.json") requests_mock.get(f"{BASE_URL}ingestion/actions/", json=mock_response) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) args = { "page": 1, "page_size": 1, "actions_type": "manual", "object_type": "indicator", } response = get_actions_command(client, args) assert response.outputs == mock_response assert response.outputs_prefix == "CTIX.Action" assert isinstance(response.raw_response, dict) def test_add_indicator_as_false_positive_command(requests_mock): mock_response = util_load_json("test_data/add_indicator_as_false_positive.json") requests_mock.post( f"{BASE_URL}ingestion/threat-data/bulk-action/false_positive/", json=mock_response, ) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) args = {"object_ids": "foo", "object_type": "indicator"} response = add_indicator_as_false_positive_command(client, args) assert response.outputs == mock_response assert response.outputs_prefix == "CTIX.IndicatorFalsePositive" assert isinstance(response.raw_response, dict) def test_add_ioc_manual_review_command(requests_mock): mock_response = util_load_json("test_data/ioc_manual_review.json") requests_mock.post( f"{BASE_URL}ingestion/threat-data/bulk-action/manual_review/", json=mock_response, ) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) args = {"object_ids": "foo", "object_type": "indicator"} response = add_ioc_manual_review_command(client, args) assert response.outputs == mock_response assert response.outputs_prefix == "CTIX.IOCManualReview" assert isinstance(response.raw_response, dict) assert len(response.raw_response) == 1 def test_deprecate_ioc_command(requests_mock): mock_response = util_load_json("test_data/deprecate_ioc.json") requests_mock.post(f"{BASE_URL}ingestion/threat-data/bulk-action/deprecate/", json=mock_response) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) args = {"object_ids": "foo", "object_type": "indicator"} response = deprecate_ioc_command(client, args) assert response.outputs == mock_response assert response.outputs_prefix == "CTIX.DeprecateIOC" assert isinstance(response.raw_response, dict) assert len(response.raw_response) == 1 def test_add_analyst_tlp_command(requests_mock): mock_response = util_load_json("test_data/add_analyst_tlp.json") requests_mock.post(f"{BASE_URL}ingestion/threat-data/action/analyst_tlp/", json=mock_response) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) args = { "object_id": "foo", "object_type": "indicator", "data": '{"analyst_tlp":"GREEN"}', } response = add_analyst_tlp_command(client, args) assert response.outputs == mock_response assert response.outputs_prefix == "CTIX.AddAnalystTLP" assert isinstance(response.raw_response, dict) assert len(response.raw_response) == 1 def test_add_analyst_score_command(requests_mock): mock_response = util_load_json("test_data/add_analyst_score.json") requests_mock.post(f"{BASE_URL}ingestion/threat-data/action/analyst_score/", json=mock_response) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) args = { "object_id": "foo", "object_type": "indicator", "data": '{"analyst_score":10}', } response = add_analyst_score_command(client, args) assert response.outputs == mock_response assert response.outputs_prefix == "CTIX.AddAnalystScore" assert isinstance(response.raw_response, dict) assert len(response.raw_response) == 1 def test_saved_result_set_command(requests_mock): mock_response = util_load_json("test_data/saved_result_set.json") requests_mock.get(f"{BASE_URL}ingestion/rules/save_result_set/", json=mock_response) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) args = {"page": 1, "page_size": 1, "label_name": "test", "query": "type=indicator", "version": "v2"} response = saved_result_set_command(client, args) assert response.outputs == mock_response assert response.outputs_prefix == "CTIX.SavedResultSet" assert isinstance(response.outputs.get("results", [])[0], dict) assert len(response.outputs.get("results", [])[0]) == 8 def test_add_tag_indicator_updation_command(requests_mock): mock_response = util_load_json("test_data/add_tag_indicator.json") mock_response_get = util_load_json("test_data/get_indicator_tags.json") requests_mock.get(f"{BASE_URL}ingestion/threat-data/indicator/foo/quick-actions/", json=mock_response_get) requests_mock.post(f"{BASE_URL}ingestion/threat-data/bulk-action/add_tag/", json=mock_response) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) args = { "page": 1, "page_size": 1, "object_id": "foo", "object_type": "indicator", "tag_id": "foo, bar", "q": "", } response = tag_indicator_updation_command(client, args, operation="add_tag_indicator") assert response.outputs == mock_response assert response.outputs_prefix == "CTIX.TagUpdation" assert isinstance(response.raw_response, dict) assert len(response.raw_response) == 1 def test_remove_tag_indicator_updation_command(requests_mock): mock_response = util_load_json("test_data/add_tag_indicator.json") mock_response_get = util_load_json("test_data/get_indicator_tags.json") requests_mock.get( f"{BASE_URL}ingestion/threat-data/indicator/foo/quick-actions/", json=mock_response_get, ) requests_mock.post(f"{BASE_URL}ingestion/threat-data/bulk-action/remove_tag/", json=mock_response) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) args = { "page": 1, "page_size": 1, "object_id": "foo", "object_type": "indicator", "tag_id": "foo,bar", "q": "", } response = tag_indicator_updation_command(client, args, operation="remove_tag_from_indicator") assert response.outputs == mock_response assert response.outputs_prefix == "CTIX.TagUpdation" assert isinstance(response.raw_response, dict) assert len(response.raw_response) == 1 def test_search_for_tag_command(requests_mock): mock_response = util_load_json("test_data/search_for_tag.json") requests_mock.get(f"{BASE_URL}ingestion/tags/", json=mock_response) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) args = { "page": 1, "page_size": 1, } response = search_for_tag_command(client, args) assert response.outputs == mock_response assert response.outputs_prefix == "CTIX.SearchTag" assert isinstance(response.raw_response, dict) assert len(response.raw_response.get("results", [])) == 1 def test_get_indicator_details_command(requests_mock): mock_response = util_load_json("test_data/get_indicator_details.json") requests_mock.get(f"{BASE_URL}ingestion/threat-data/indicator/foo/basic/", json=mock_response) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) args = {"page": 1, "page_size": 1, "object_type": "indicator", "object_id": "foo"} response = get_indicator_details_command(client, args) assert response.outputs == mock_response assert response.outputs_prefix == "CTIX.IndicatorDetails" assert isinstance(response.raw_response, dict) assert len(response.raw_response) == 34 def test_get_indicator_tags_command(requests_mock): mock_response = util_load_json("test_data/get_indicator_tags.json") requests_mock.get( f"{BASE_URL}ingestion/threat-data/indicator/foo/quick-actions/", json=mock_response, ) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) args = {"page": 1, "page_size": 1, "object_type": "indicator", "object_id": "foo"} response = get_indicator_tags_command(client, args) assert response.outputs == mock_response assert response.outputs_prefix == "CTIX.IndicatorTags" assert isinstance(response.raw_response, dict) assert len(response.raw_response) == 13 def test_get_indicator_relations_command(requests_mock): mock_response = util_load_json("test_data/get_indicator_relations.json") requests_mock.get(f"{BASE_URL}ingestion/threat-data/indicator/foo/relations/", json=mock_response) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) args = {"page": 1, "page_size": 1, "object_type": "indicator", "object_id": "foo"} response = get_indicator_relations_command(client, args) assert response.outputs == mock_response assert response.outputs_prefix == "CTIX.IndicatorRelations" assert isinstance(response.raw_response, dict) assert len(response.raw_response.get("results", [])) == 1 def test_get_indicator_observations_command(requests_mock): mock_response = util_load_json("test_data/get_indicator_observations.json") requests_mock.get(f"{BASE_URL}ingestion/threat-data/source-references/", json=mock_response) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) args = {"page": 1, "page_size": 1, "object_type": "indicator", "object_id": "foo"} response = get_indicator_observations_command(client, args) assert response.outputs == mock_response assert response.outputs_prefix == "CTIX.IndicatorObservations" assert isinstance(response.raw_response, dict) assert len(response.raw_response.get("results", [])) == 1 def test_get_conversion_feed_source_command(requests_mock): mock_response = util_load_json("test_data/get_conversion_feed_source.json") requests_mock.get(f"{BASE_URL}conversion/feed-sources/", json=mock_response) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) args = {"page": 1, "page_size": 1, "object_type": "indicator", "object_id": "foo"} response = get_conversion_feed_source_command(client, args) assert response.outputs.get("results", [])[0] == mock_response.get("results", [None])[0] assert response.outputs_prefix == "CTIX.ConversionFeedSource" assert isinstance(response.raw_response, dict) assert len(response.raw_response.get("results", [])) == 10 def test_get_lookup_threat_data_command(requests_mock): mock_response = util_load_json("test_data/get_lookup_threat_data.json") requests_mock.post(f"{BASE_URL}ingestion/threat-data/list/", json=mock_response) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) args = { "page": 1, "page_size": 1, "object_type": "indicator", "object_names": "foo,bar", } response = get_lookup_threat_data_command(client, args) assert response[0].outputs == mock_response["results"][0] assert response[0].outputs_prefix == "CTIX.ThreatDataLookup" assert isinstance(response[0].raw_response, dict) assert len(response[0].raw_response) == 37 def test_domain(requests_mock): mock_response = util_load_json("test_data/domain.json") requests_mock.post(f"{BASE_URL}ingestion/threat-data/list/", json=mock_response) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) args = { "domain": "google.com", "page": 1, "page_size": 1, "object_type": "indicator", "object_names": "foo,bar", } response = domain(client, args) assert response[0].outputs == mock_response["results"][0] assert response[0].outputs_prefix == "CTIX.ThreatDataLookup" assert isinstance(response[0].raw_response, dict) assert len(response[0].raw_response) == 37 def test_url(requests_mock): mock_response = util_load_json("test_data/url.json") requests_mock.post(f"{BASE_URL}ingestion/threat-data/list/", json=mock_response) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) args = { "url": "https://example.com/", "page": 1, "page_size": 1, "object_type": "indicator", "object_names": "foo,bar", } response = url(client, args) assert response[0].outputs == mock_response["results"][0] assert response[0].outputs_prefix == "CTIX.ThreatDataLookup" assert isinstance(response[0].raw_response, dict) assert len(response[0].raw_response) == 37 def test_ip(requests_mock): mock_response = util_load_json("test_data/ip.json") requests_mock.post(f"{BASE_URL}ingestion/threat-data/list/", json=mock_response) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) args = { "ip": "1.2.3.4", "page": 1, "page_size": 1, "object_type": "indicator", "object_names": "foo,bar", } response = ip(client, args) assert response[0].outputs == mock_response["results"][0] assert response[0].outputs_prefix == "CTIX.ThreatDataLookup" assert isinstance(response[0].raw_response, dict) assert len(response[0].raw_response) == 37 def test_file(requests_mock): mock_response = util_load_json("test_data/file.json") requests_mock.post(f"{BASE_URL}ingestion/threat-data/list/", json=mock_response) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) args = { "file": "a6a91e61a729bb4c12cc3db3eb9ea746", "page": 1, "page_size": 1, "object_type": "indicator", "object_names": "foo,bar", } response = file(client, args) assert response[0].outputs == mock_response["results"][0] assert response[0].outputs_prefix == "CTIX.ThreatDataLookup" assert isinstance(response[0].raw_response, dict) assert len(response[0].raw_response) == 37 def test_get_all_notes(requests_mock): mock_response = util_load_json("test_data/get_all_notes.json") requests_mock.get(f"{BASE_URL}ingestion/notes/", json=mock_response) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) args = { "page": 1, "page_size": 10, } response = get_all_notes(client, args) assert response.outputs == mock_response assert response.outputs_prefix == "CTIX.Note" assert isinstance(response.raw_response, dict) assert len(response.raw_response.get("results", [])) == 10 def test_get_note_details(requests_mock): mock_response = util_load_json("test_data/get_note_details.json") id = "b1800a11-7fa5-423e-93bf-f8ef8d3890a4" requests_mock.get(f"{BASE_URL}ingestion/notes/{id}/", json=mock_response) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) args = {"id": id} response = get_note_details(client, args) assert response.outputs == mock_response assert response.outputs_prefix == "CTIX.Note" assert isinstance(response.raw_response, dict) assert len(response.raw_response) == 11 def test_create_note(requests_mock): mock_response = util_load_json("test_data/create_note.json") requests_mock.post(f"{BASE_URL}ingestion/notes/", json=mock_response) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) args = { "text": "this note will have this text", "object_id": "ba82b524-15b3-4071-8008-e58754f8d134", "object_type": "indicator", } response = create_note(client, args) assert response.outputs == mock_response assert response.outputs_prefix == "CTIX.Note" assert isinstance(response.raw_response, dict) assert len(response.raw_response) == 11 def test_update_note(requests_mock): mock_response = util_load_json("test_data/update_note.json") id = "04bb5f2c-78a6-4e84-82ae-011666733998" requests_mock.put(f"{BASE_URL}ingestion/notes/{id}/", json=mock_response) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) args = { "id": id, "text": "this is the new text", "object_id": "ba82b524-15b3-4071-8008-e58754f8d134", "object_type": "indicator", } response = update_note(client, args) assert response.outputs == mock_response assert response.outputs_prefix == "CTIX.Note" assert isinstance(response.raw_response, dict) assert len(response.raw_response) == 11 def test_delete_note(requests_mock): mock_response = util_load_json("test_data/delete_note.json") id = "04bb5f2c-78a6-4e84-82ae-011666733998" requests_mock.delete(f"{BASE_URL}ingestion/notes/{id}/", json=mock_response) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) args = {"id": id} response = delete_note(client, args) assert response.outputs == mock_response assert response.outputs_prefix == "CTIX.Note" assert isinstance(response.raw_response, dict) assert len(response.raw_response) == 1 def test_make_request_get(requests_mock): mock_response = util_load_json("test_data/make_request_get.json") requests_mock.get(f"{BASE_URL}ingestion/notes/", json=mock_response) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) args = { "type": "GET", "endpoint": "ingestion/notes/", "page": 2, "page_size": 10, } response = make_request(client, args) assert response[0].outputs == mock_response["results"][0] assert response[0].outputs_prefix == "CTIX.Request.GET.ingestion/notes/" assert isinstance(response[0].raw_response, dict) assert len(response[0].raw_response) == 11 def test_make_request_post(requests_mock): mock_response = util_load_json("test_data/make_request_post.json") requests_mock.post(f"{BASE_URL}ingestion/notes/", json=mock_response) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) args = { "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\" }""", } response = make_request(client, args) assert response[0].outputs == mock_response assert response[0].outputs_prefix == "CTIX.Request.POST.ingestion/notes/" assert isinstance(response[0].raw_response, dict) assert len(response[0].raw_response) == 11 def test_make_request_put(requests_mock): mock_response = util_load_json("test_data/make_request_put.json") requests_mock.put(f"{BASE_URL}ingestion/notes/40c57c4a-1b5d-4cb4-bd89-d146c0d30ed4/", json=mock_response) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) args = { "type": "PUT", "endpoint": "ingestion/notes/40c57c4a-1b5d-4cb4-bd89-d146c0d30ed4/", "body": """{ \"text\": \"this is the new text\", \"type\": \"threatdata\", \"meta_data\": { \"component\": \"threatdata\", \"object_id\": \"ba82b524-15b3-4071-8008-e58754f8d134\", \"type\": \"indicator\" }, \"object_id\": \"ba82b524-15b3-4071-8008-e58754f8d134\" }""", } response = make_request(client, args) assert response[0].outputs == mock_response assert response[0].outputs_prefix == "CTIX.Request.PUT.ingestion/notes/40c57c4a-1b5d-4cb4-bd89-d146c0d30ed4/" assert isinstance(response[0].raw_response, dict) assert len(response[0].raw_response) == 11 def test_make_request_delete(requests_mock): mock_response = util_load_json("test_data/make_request_delete.json") requests_mock.delete(f"{BASE_URL}ingestion/notes/40c57c4a-1b5d-4cb4-bd89-d146c0d30ed4/", json=mock_response) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) args = { "type": "DELETE", "endpoint": "ingestion/notes/40c57c4a-1b5d-4cb4-bd89-d146c0d30ed4/", } response = make_request(client, args) assert response[0].outputs == mock_response assert response[0].outputs_prefix == "CTIX.Request.DELETE.ingestion/notes/40c57c4a-1b5d-4cb4-bd89-d146c0d30ed4/" assert isinstance(response[0].raw_response, dict) assert len(response[0].raw_response) == 1 def test_cve_command(requests_mock): mock_threat_list_response = util_load_json("test_data/get_cve_threat_data.json") requests_mock.post(f"{BASE_URL}ingestion/threat-data/list/", json=mock_threat_list_response) obj_id = mock_threat_list_response["results"][0]["id"] mock_product_details_response = util_load_json("test_data/get_vulnerability_product_details.json") requests_mock.get( f"{BASE_URL}ingestion/threat-data/vulnerability/{obj_id}/product-details/", json=mock_product_details_response ) source_id = mock_product_details_response["results"][0]["source"]["id"] mock_cvss_score_response = util_load_json("test_data/get_cvss_score.json") requests_mock.get(f"{BASE_URL}ingestion/threat-data/vulnerability/{obj_id}/cvss-score/", json=mock_cvss_score_response) mock_source_description_response = util_load_json("test_data/get_vulnerability_source_description.json") requests_mock.get( f"{BASE_URL}ingestion/threat-data/vulnerability/{obj_id}/source-description/?source_id={source_id}", json=mock_source_description_response, ) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) args = { "cve": "CVE-2023-33250", } response = cve_command(client, args) assert response[0].outputs["uuid"] == obj_id assert response[0].outputs_prefix == "CTIX.VulnerabilityLookup" assert isinstance(response[0].outputs, dict) assert len(response[0].outputs) == 10 def test_bulk_ioc_lookup_advanced_by_value(requests_mock): """Test bulk IOC lookup advanced using indicator values.""" mock_response = util_load_json("test_data/bulk_ioc_lookup_advanced.json") requests_mock.post(f"{BASE_URL}ingestion/openapi/bulk-lookup/indicator/", json=mock_response) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) args = { "object_type": "indicator", "values": "1.1.1.1,www.facebook.com", } response = bulk_ioc_lookup_advanced_command(client, args) assert response.outputs_prefix == "CTIX.BulkIOCLookupAdvanced" results = response.outputs.get("results", []) assert isinstance(results, list) assert len(results) == 2 assert results[0]["id"] == "3fa85f64-5717-4562-b3fc-2c963f66afa6" assert results[0]["name"] == "1.1.1.1" assert results[0]["ioc_type"] == "ipv4-addr" assert results[0]["confidence_score"] == 80 assert results[1]["id"] == "8a5f8c2d-1234-4abc-def0-1a2b3c4d5e6f" assert results[1]["name"] == "www.facebook.com" assert results[1]["is_whitelisted"] is True assert isinstance(response.raw_response, dict) def test_bulk_ioc_lookup_advanced_by_object_id(requests_mock): """Test bulk IOC lookup advanced using object IDs with optional params.""" mock_response = util_load_json("test_data/bulk_ioc_lookup_advanced.json") requests_mock.post(f"{BASE_URL}ingestion/openapi/bulk-lookup/indicator/", json=mock_response) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) args = { "object_type": "indicator", "object_ids": "3fa85f64-5717-4562-b3fc-2c963f66afa6,8a5f8c2d-1234-4abc-def0-1a2b3c4d5e6f", "enrichment_data": "true", "relation_data": "true", "enrichment_tools": "AbuseIPDB", "fields": "relations,enrichment_data", } response = bulk_ioc_lookup_advanced_command(client, args) assert response.outputs_prefix == "CTIX.BulkIOCLookupAdvanced" results = response.outputs.get("results", []) assert isinstance(results, list) assert len(results) == 2 assert results[0]["object_type"] == "indicator" assert results[1]["is_reviewed"] is True assert isinstance(response.raw_response, dict) def test_bulk_ioc_lookup_advanced_no_results(requests_mock): """Test bulk IOC lookup advanced when no matching results are returned.""" requests_mock.post(f"{BASE_URL}ingestion/openapi/bulk-lookup/indicator/", json=[]) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) args = { "object_type": "indicator", "values": "not.a.real.ioc", } response = bulk_ioc_lookup_advanced_command(client, args) assert response.outputs is None assert response.readable_output == "No results were found" """ ============================================= Tests for fetch-incidents, fetch-indicators, helper functions, and enrichment ============================================= """ class TestConfidenceToDbotScore: """Test confidence_to_dbot_score helper.""" def test_none_returns_none_score(self): assert confidence_to_dbot_score(None) == 0 def test_string_returns_none_score(self): assert confidence_to_dbot_score("NA") == 0 def test_high_confidence_returns_bad(self): assert confidence_to_dbot_score(80) == 3 assert confidence_to_dbot_score(100) == 3 def test_medium_confidence_returns_suspicious(self): assert confidence_to_dbot_score(50) == 2 assert confidence_to_dbot_score(79) == 2 def test_low_confidence_returns_none(self): assert confidence_to_dbot_score(0) == 0 assert confidence_to_dbot_score(49) == 0 class TestNormalizeIndicatorType: """Test normalize_indicator_type helper.""" def test_ipv4(self): assert normalize_indicator_type("ipv4-addr") == "IP" def test_domain(self): assert normalize_indicator_type("domain-name") == "Domain" def test_url(self): assert normalize_indicator_type("url") == "URL" def test_file_hash(self): assert normalize_indicator_type("SHA-256") == "File" def test_unknown_type(self): assert normalize_indicator_type("unknown-type") == "Custom Indicator" def test_none_type(self): assert normalize_indicator_type(None) == "Custom Indicator" class TestMapReportSeverity: """Test map_report_severity helper.""" def test_high_severity_string(self): report = {"risk_severity": "HIGH"} assert map_report_severity(report) == 3 def test_medium_severity_string(self): report = {"risk_severity": "MEDIUM"} assert map_report_severity(report) == 2 def test_low_severity_string(self): report = {"risk_severity": "LOW"} assert map_report_severity(report) == 1 def test_critical_severity_string(self): report = {"risk_severity": "CRITICAL"} assert map_report_severity(report) == 4 def test_high_confidence_fallback(self): report = {"risk_severity": "UNKNOWN", "confidence_score": 85} assert map_report_severity(report) == 3 def test_medium_confidence_fallback(self): report = {"risk_severity": None, "confidence_score": 55} assert map_report_severity(report) == 2 def test_unknown_default(self): report = {} assert map_report_severity(report) == 0 class TestMapReportToIncident: """Test map_report_to_incident helper.""" def test_basic_mapping(self): report = { "id": "report-001", "name": "Test Report", "description": "A test report.", "created": 1700000000, "modified": 1700000100, "risk_severity": "HIGH", "tlp": "AMBER", "tags": [{"id": "t1", "name": "apt29"}], "sources": [{"id": "s1", "name": "ThreatFeed"}], } incident = map_report_to_incident(report) assert incident["name"] == "CTIX Intel: Test Report" assert incident["severity"] == 3 assert incident["dbotMirrorId"] == "report-001" assert incident["occurred"] == "2023-11-14T22:13:20Z" assert len(incident["labels"]) == 1 assert incident["labels"][0]["value"] == "apt29" # Ensure rawJSON is valid JSON raw = json.loads(incident["rawJSON"]) assert raw["id"] == "report-001" def test_missing_fields(self): report = {"id": "report-empty"} incident = map_report_to_incident(report) assert incident["occurred"] == "" assert incident["severity"] == 0 assert incident["labels"] == [] def test_string_tags(self): report = {"id": "report-str-tags", "tags": ["tag1", "tag2"], "created": 0} incident = map_report_to_incident(report) assert len(incident["labels"]) == 2 assert incident["labels"][0]["value"] == "tag1" class TestMapCtixIndicatorToXsoar: """Test map_ctix_indicator_to_xsoar helper.""" def test_basic_ip_indicator(self): indicator = { "name": "1.2.3.4", "ioc_type": "ipv4-addr", "confidence_score": 85, "source_tlp": "AMBER", "first_seen": 1700000000, "last_seen": 1700000100, "id": "ind-001", "ctix_created": 1700000001, "ctix_modified": 1700000001, "tags": [{"name": "malware"}], "sources": [{"name": "ThreatFeed"}], "description": "Malicious IP", "is_false_positive": False, "is_deprecated": False, "is_reviewed": True, "is_whitelisted": False, } result = map_ctix_indicator_to_xsoar(indicator, "B - Usually reliable") assert result["value"] == "1.2.3.4" assert result["type"] == "IP" assert result["score"] == 3 # confidence 85 -> BAD assert result["fields"]["trafficlightprotocol"] == "AMBER" assert "malware" in result["fields"]["tags"] assert result["fields"]["reportedby"] == "ThreatFeed" def test_flag_fields_mapped(self): """All five CTIX status flags map to XSOAR fields when True.""" indicator = { "name": "1.2.3.4", "ioc_type": "ipv4-addr", "confidence_score": 85, "sources": [], "tags": [], "is_false_positive": True, "is_deprecated": True, "is_reviewed": True, "is_whitelist": True, "is_revoked": True, } result = map_ctix_indicator_to_xsoar(indicator, "B - Usually reliable") assert result["fields"]["ctixisfalsepositive"] is True assert result["fields"]["ctixisdeprecated"] is True assert result["fields"]["ctixisreviewed"] is True assert result["fields"]["ctixiswhitelisted"] is True assert result["fields"]["ctixisrevoked"] is True def test_flag_fields_false_are_kept(self): """False flags are still mapped (assign_params only drops None/empty), so un-flagging in CTIX clears the TIM field on the next fetch.""" indicator = { "name": "1.2.3.4", "ioc_type": "ipv4-addr", "confidence_score": 85, "sources": [], "tags": [], "is_revoked": False, } result = map_ctix_indicator_to_xsoar(indicator, "B - Usually reliable") assert result["fields"]["ctixisrevoked"] is False def test_flag_fields_absent_are_omitted(self): """Indicators without flag keys don't get flag fields (None is dropped).""" indicator = { "name": "1.2.3.4", "ioc_type": "ipv4-addr", "confidence_score": 85, "sources": [], "tags": [], } result = map_ctix_indicator_to_xsoar(indicator, "B - Usually reliable") assert "ctixisrevoked" not in result["fields"] assert "ctixisdeprecated" not in result["fields"] def test_domain_indicator_with_dict_ioc_type(self): indicator = { "name": "evil.example.com", "ioc_type": {"type": "domain-name", "attribute_field": "value"}, "ctix_score": 55, "source_tlp": "GREEN", "sources": [], "tags": ["phishing"], "id": "ind-002", } result = map_ctix_indicator_to_xsoar(indicator, "C - Fairly reliable") assert result["value"] == "evil.example.com" assert result["type"] == "Domain" assert result["score"] == 2 # confidence 55 -> SUSPICIOUS def test_indicator_with_enrichment(self): indicator = { "name": "1.2.3.4", "ioc_type": "ipv4-addr", "confidence_score": 90, "id": "ind-001", "sources": [], "tags": [], } enrichment = { "description": "Enriched description from bulk lookup", "relations": {"related-to": [{"name": "evil.example.com", "type": "domain-name"}]}, "enrichment_data": [{"tool_name": "VT", "result": {"malicious": 10}}], } result = map_ctix_indicator_to_xsoar(indicator, "B - Usually reliable", enrichment) assert result["fields"]["description"] == "Enriched description from bulk lookup" assert result["relationships"] is not None assert len(result["relationships"]) == 1 assert "ctixenrichment" in result["fields"] def test_indicator_with_custom_scores(self): indicator = { "name": "5.6.7.8", "ioc_type": "ipv4-addr", "confidence_score": 85, "custom_scores": {"x_ctix_customscore_2": 34}, "sources": [], "tags": [], } result = map_ctix_indicator_to_xsoar(indicator, "B - Usually reliable") parsed_scores = json.loads(result["fields"].get("ctixcustomscores", "{}")) assert parsed_scores.get("x_ctix_customscore_2") == 34 def test_indicator_with_no_value(self): indicator = {"ioc_type": "ipv4-addr", "sources": [], "tags": []} result = map_ctix_indicator_to_xsoar(indicator, "C - Fairly reliable") assert not result.get("value") # empty string is stripped by assign_params def test_relations_string_items_use_rel_type_for_entity_b_type(self): """Relations with plain-string items (bulk IOC lookup base format) resolve entity_b_type from the relation key via _STIX_SDO_TO_XSOAR_ENTITY_TYPE, and always use 'related-to' as the relationship verb (the key is a STIX object type, not a relationship verb).""" indicator = { "name": "1.2.3.4", "ioc_type": "ipv4-addr", "confidence_score": 64, "sources": [], "tags": [], "relations": { "malware": ["Adaptix"], "report": ["Adaptix_20260209"], }, } result = map_ctix_indicator_to_xsoar(indicator, "B - Usually reliable") assert result["relationships"] is not None assert len(result["relationships"]) == 2 # All plain-string relations use "related-to" as the relationship verb; # identify each by its entityBType which is derived from the dict key. malware_rel = next((r for r in result["relationships"] if r.get("entityBType") == "Malware"), None) assert malware_rel is not None assert malware_rel["name"] == "related-to" assert malware_rel["entityA"] == "1.2.3.4" assert malware_rel["entityB"] == "Adaptix" report_rel = next((r for r in result["relationships"] if r.get("entityBType") == "Report"), None) assert report_rel is not None assert report_rel["name"] == "related-to" assert report_rel["entityB"] == "Adaptix_20260209" def test_relations_multiple_string_items_per_type(self): """Multiple string values under one relation key each produce a separate relationship.""" indicator = { "name": "1.2.3.4", "ioc_type": "ipv4-addr", "confidence_score": 80, "sources": [], "tags": [], "relations": { "malware": ["MalwareA", "MalwareB", "MalwareC"], }, } result = map_ctix_indicator_to_xsoar(indicator, "B - Usually reliable") assert result["relationships"] is not None assert len(result["relationships"]) == 3 entity_b_values = {r["entityB"] for r in result["relationships"]} assert entity_b_values == {"MalwareA", "MalwareB", "MalwareC"} for rel in result["relationships"]: assert rel["entityBType"] == "Malware" assert rel["name"] == "related-to" # plain-string format always uses "related-to" def test_relations_dict_items_use_embedded_type(self): """Relations with dict items (enrichment format) resolve entity_b_type via _STIX_SDO_TO_XSOAR_ENTITY_TYPE using the embedded 'type' field.""" indicator = { "name": "1.2.3.4", "ioc_type": "ipv4-addr", "confidence_score": 80, "sources": [], "tags": [], "relations": { "related-to": [ {"name": "evil.example.com", "type": "domain-name"}, ], }, } result = map_ctix_indicator_to_xsoar(indicator, "B - Usually reliable") assert result["relationships"] is not None assert len(result["relationships"]) == 1 rel = result["relationships"][0] assert rel["entityB"] == "evil.example.com" assert rel["entityBType"] == "Domain" assert rel["name"] == "related-to" def test_relations_unknown_type_key_falls_back_to_indicator_entity_b_type(self): """A relation key not present in _STIX_SDO_TO_XSOAR_ENTITY_TYPE falls back to 'Indicator' as the entityBType rather than raising an error.""" indicator = { "name": "1.2.3.4", "ioc_type": "ipv4-addr", "confidence_score": 80, "sources": [], "tags": [], "relations": { "custom-unknown-relationship": ["TargetObject"], }, } result = map_ctix_indicator_to_xsoar(indicator, "B - Usually reliable") assert result["relationships"] is not None assert len(result["relationships"]) == 1 rel = result["relationships"][0] assert rel["entityB"] == "TargetObject" assert rel.get("entityBType") == "Indicator" def test_relations_empty_or_null_produces_no_relationships(self): """Empty dict, None, and missing relations key all result in no relationships.""" for relations_value in [{}, None]: indicator = { "name": "1.2.3.4", "ioc_type": "ipv4-addr", "confidence_score": 80, "sources": [], "tags": [], "relations": relations_value, } result = map_ctix_indicator_to_xsoar(indicator, "B - Usually reliable") assert not result.get("relationships"), f"Expected no relationships for relations={relations_value!r}" # Missing key entirely indicator_no_key = {"name": "1.2.3.4", "ioc_type": "ipv4-addr", "confidence_score": 80, "sources": [], "tags": []} result = map_ctix_indicator_to_xsoar(indicator_no_key, "B - Usually reliable") assert not result.get("relationships") class TestFetchIncidents: """Test fetch_incidents function.""" def test_first_run(self, requests_mock): mock_response = util_load_json("test_data/fetch_incidents_reports.json") requests_mock.post(f"{BASE_URL}ingestion/threat-data/list/", json=mock_response) _register_fetch_incidents_bulk_mock(requests_mock) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) params = {"first_fetch": "4320"} last_run = {} next_run, incidents = fetch_incidents(client, params, last_run) assert len(incidents) == 3 assert incidents[0]["name"] == "CTIX Intel: APT29 Campaign Analysis" assert incidents[0]["severity"] == 3 # HIGH assert incidents[1]["name"] == "CTIX Intel: Ransomware Trend Report Q4" assert incidents[1]["severity"] == 2 # MEDIUM assert incidents[2]["name"] == "CTIX Intel: Low Confidence Observation" assert isinstance(next_run.get("last_fetch_time"), int) # Allow a small tolerance for clock drift between fetch and assertion assert abs(next_run["last_fetch_time"] - int(datetime.now(UTC).timestamp())) <= 5 def test_first_run_uses_first_fetch_minutes_in_cql(self, requests_mock): """Empty LastRun: CQL lower bound is now - first_fetch minutes, not epoch 0.""" mock_response = util_load_json("test_data/fetch_incidents_reports.json") requests_mock.post(f"{BASE_URL}ingestion/threat-data/list/", json=mock_response) _register_fetch_incidents_bulk_mock(requests_mock) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) params = {"first_fetch": "60"} last_run = {} fetch_incidents(client, params, last_run) raw_body = requests_mock.request_history[0].body if isinstance(raw_body, bytes): raw_body = raw_body.decode() body = json.loads(raw_body) query = body["query"] match = re.search(r'ctix_modified >= "(\d+)"', query) assert match is not None expected_low = int(datetime.now(UTC).timestamp()) - 60 * 60 assert abs(int(match.group(1)) - expected_low) <= 5 def test_no_legacy_deduplication(self, requests_mock): mock_response = util_load_json("test_data/fetch_incidents_reports.json") requests_mock.post(f"{BASE_URL}ingestion/threat-data/list/", json=mock_response) _register_fetch_incidents_bulk_mock(requests_mock) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) params = {"first_fetch": "4320"} # Legacy last_fetch_* keys are accepted for compatibility but no longer drive dedup logic. last_run = { "last_fetch_time": 1700000101, "last_fetch_ids": ["report-001-aaaa-bbbb-cccc-ddddeeee0001"], } next_run, incidents = fetch_incidents(client, params, last_run) assert len(incidents) == 3 def test_empty_response(self, requests_mock): requests_mock.post( f"{BASE_URL}ingestion/threat-data/list/", json={"results": [], "total": 0}, ) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) params = {"first_fetch": "4320"} last_run = {"last_fetch_time": 1700000000} next_run, incidents = fetch_incidents(client, params, last_run) assert len(incidents) == 0 # fetch_incidents uses the fetch start time as the HWM when no new results # Allow a small tolerance for clock drift rather than strict equality assert isinstance(next_run.get("last_fetch_time"), int) assert abs(next_run["last_fetch_time"] - int(datetime.now(UTC).timestamp())) <= 5 def test_max_fetch_limits_incidents_and_page_size(self, requests_mock): mock_response = util_load_json("test_data/fetch_incidents_reports.json") requests_mock.post(f"{BASE_URL}ingestion/threat-data/list/", json=mock_response) _register_fetch_incidents_bulk_mock(requests_mock) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) params = {"max_fetch": "2", "first_fetch": "4320"} last_run = {} next_run, incidents = fetch_incidents(client, params, last_run) assert len(incidents) == 2 req = requests_mock.request_history[0] qs = parse_qs(urlparse(req.url).query) assert qs.get("page_size") == ["2"] def test_fetch_incidents_next_page_stores_checkpoint(self, requests_mock): """One page per run: API `next` is set → LastRun advances page_number for the next interval.""" full = util_load_json("test_data/fetch_incidents_reports.json") page1 = { **full, "results": full["results"][:1], "next": "https://example.com/ctixapi/ingestion/threat-data/list/?page=2&page_size=100", } requests_mock.post(f"{BASE_URL}ingestion/threat-data/list/", json=page1) _register_fetch_incidents_bulk_mock(requests_mock) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) params = {"first_fetch": "4320"} next_run, incidents = fetch_incidents(client, params, {}) assert len(incidents) == 1 assert next_run.get("page_number") == 2 def test_fetch_incidents_second_interval_completes_when_no_next(self, requests_mock): """Resuming at page 2 with no `next` completes the sweep and clears page_number.""" full = util_load_json("test_data/fetch_incidents_reports.json") page2 = {**full, "results": full["results"][1:2], "next": None} requests_mock.post(f"{BASE_URL}ingestion/threat-data/list/", json=page2) _register_fetch_incidents_bulk_mock(requests_mock) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) params = {"first_fetch": "4320"} last_run = {"page_number": 2, "last_run_date": 1700000000} next_run, incidents = fetch_incidents(client, params, last_run) assert len(incidents) == 1 assert next_run.get("page_number") == 0 req = requests_mock.request_history[0] qs = parse_qs(urlparse(req.url).query) assert qs.get("page") == ["2"] def test_fetch_incidents_empty_results_with_next_advances_page(self, requests_mock): """Empty `results` but truthy `next` must not complete the sweep (avoid stuck pagination).""" requests_mock.post( f"{BASE_URL}ingestion/threat-data/list/", json={"results": [], "next": "https://example.com/ctixapi/ingestion/threat-data/list/?page=2", "total": 0}, ) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) params = {"first_fetch": "4320"} next_run, incidents = fetch_incidents(client, params, {}) assert len(incidents) == 0 assert next_run.get("page_number") == 2 def test_custom_cql_query_used_as_base(self, requests_mock): """incident_fetch_query replaces the default base query; time-window is appended.""" mock_response = util_load_json("test_data/fetch_incidents_reports.json") requests_mock.post(f"{BASE_URL}ingestion/threat-data/list/", json=mock_response) _register_fetch_incidents_bulk_mock(requests_mock) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) params = { "first_fetch": "4320", "incident_fetch_query": 'type = "report" AND severity = "HIGH"', } last_run = {"last_fetch_time": 1700000000} next_run, incidents = fetch_incidents(client, params, last_run) assert len(incidents) == 3 def test_default_query_when_no_custom_cql(self, requests_mock): """When incident_fetch_query is absent the default 'type = \"report\"' query is sent.""" mock_response = util_load_json("test_data/fetch_incidents_reports.json") requests_mock.post(f"{BASE_URL}ingestion/threat-data/list/", json=mock_response) _register_fetch_incidents_bulk_mock(requests_mock) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) params = {"first_fetch": "4320"} # No incident_fetch_query last_run = {"last_fetch_time": 1700000000} fetch_incidents(client, params, last_run) def test_custom_cql_query_on_first_run(self, requests_mock): """Custom query on first run: time-window derived from first_fetch is still appended.""" mock_response = util_load_json("test_data/fetch_incidents_reports.json") requests_mock.post(f"{BASE_URL}ingestion/threat-data/list/", json=mock_response) _register_fetch_incidents_bulk_mock(requests_mock) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) params = { "first_fetch": "4320", "incident_fetch_query": 'type = "report" AND tlp = "AMBER"', } last_run = {} # First run – no last_fetch_time stored next_run, incidents = fetch_incidents(client, params, last_run) # next_run must have a timestamp for subsequent runs assert next_run.get("last_fetch_time") is not None def test_custom_cql_query_results_and_state(self, requests_mock): """End-to-end: custom query fetches incidents and advances last_fetch_time.""" mock_response = util_load_json("test_data/fetch_incidents_reports.json") requests_mock.post(f"{BASE_URL}ingestion/threat-data/list/", json=mock_response) _register_fetch_incidents_bulk_mock(requests_mock) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) params = { "first_fetch": "4320", "incident_fetch_query": 'type = "report" AND confidence_score > 50', } last_run = {"last_fetch_time": 1700000000} next_run, incidents = fetch_incidents(client, params, last_run) # All three mock reports should be returned assert len(incidents) == 3 # State should update on completed sweep. assert isinstance(next_run.get("last_fetch_time"), int) class TestFetchIndicators: """Test fetch_indicators function.""" def test_first_run(self, requests_mock): mock_response = util_load_json("test_data/fetch_indicators_result_set.json") requests_mock.get(f"{BASE_URL}ingestion/rules/save_result_set/", json=mock_response) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) params = { "first_fetch": "4320", "retrieve_enriched_data": False, "integrationReliability": "C - Fairly reliable", } last_run = {} next_run, indicators = fetch_indicators(client, params, last_run) # 4 items in data assert len(indicators) == 4 assert indicators[0]["value"] == "1.2.3.4" assert indicators[0]["type"] == "IP" assert indicators[0]["score"] == 3 # confidence 85 assert indicators[0]["fields"]["ctixcustomscores"] == '{"x_ctix_customscore_2": 34}' assert indicators[1]["value"] == "evil.example.com" assert indicators[1]["type"] == "Domain" assert indicators[1]["score"] == 2 # confidence 55 assert indicators[2]["value"] == "https://malware.example.com/payload" assert indicators[2]["type"] == "URL" assert indicators[2]["score"] == 1 # severity LOW -> GOOD (1) assert next_run["last_indicator_time"] is not None def test_saved_result_set_pagination_uses_explicit_page_params(self, requests_mock): page1 = util_load_json("test_data/fetch_indicators_result_set.json") page2 = util_load_json("test_data/fetch_indicators_result_set.json") page1["next"] = "https://example.com/ctixapi/ingestion/rules/save_result_set/?page=2&page_size=100" page2["next"] = None requests_mock.get( f"{BASE_URL}ingestion/rules/save_result_set/", [{"json": page1}, {"json": page2}], ) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) params = { "first_fetch": "4320", "retrieve_enriched_data": False, "integrationReliability": "C - Fairly reliable", "saved_result_set_label": "UnitTestLabel", "saved_result_set_version": "v3", "feedFetchInterval": "12 hours", } last_run = {"page_number": 1, "last_run_date": 1700000000} next_run, indicators = fetch_indicators(client, params, last_run) assert len(indicators) == 8 assert next_run.get("page_number") == 0 first_req = requests_mock.request_history[0] second_req = requests_mock.request_history[1] first_qs = parse_qs(urlparse(first_req.url).query) second_qs = parse_qs(urlparse(second_req.url).query) assert first_qs.get("page") == ["1"] assert second_qs.get("page") == ["2"] assert first_qs.get("page_size") == ["100"] assert second_qs.get("page_size") == ["100"] assert first_qs.get("label_name") == ["UnitTestLabel"] assert second_qs.get("label_name") == ["UnitTestLabel"] assert first_qs.get("version") == ["v3"] assert second_qs.get("version") == ["v3"] assert first_qs.get("from_timestamp") == ["1700000000"] assert second_qs.get("from_timestamp") == ["1700000000"] def test_first_run_passes_from_timestamp_from_first_fetch(self, requests_mock): """Empty LastRun: saved_result_set gets from_timestamp = now - first_fetch minutes.""" mock_response = util_load_json("test_data/fetch_indicators_result_set.json") requests_mock.get(f"{BASE_URL}ingestion/rules/save_result_set/", json=mock_response) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) params = { "first_fetch": "60", "retrieve_enriched_data": False, "integrationReliability": "C - Fairly reliable", } fetch_indicators(client, params, {}) req = requests_mock.request_history[0] parsed = urlparse(req.url) qs = parse_qs(parsed.query) assert "from_timestamp" in qs from_ts = int(qs["from_timestamp"][0]) expected_low = int(datetime.now(UTC).timestamp()) - 60 * 60 assert abs(from_ts - expected_low) <= 5 def test_deduplication(self, requests_mock): """Test that duplicate indicators from the saved result set are preserved. Deduplication is performed later when indicators are submitted to XSOAR (e.g. via demisto.createIndicators). The fetch_indicators function intentionally returns all collected items from the saved result set. """ mock_response = util_load_json("test_data/fetch_indicators_result_set.json") # Add a duplicate indicator to the data dup_indicator = dict(mock_response["results"][0]["data"][0]) dup_indicator["id"] = "ind-dup-001" mock_response["results"][0]["data"].append(dup_indicator) requests_mock.get(f"{BASE_URL}ingestion/rules/save_result_set/", json=mock_response) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) params = { "first_fetch": "4320", "retrieve_enriched_data": False, "integrationReliability": "C - Fairly reliable", } next_run, indicators = fetch_indicators(client, params, {}) # Deduplication is delegated to demisto.createIndicators; fetch_indicators returns all assert len(indicators) == 5 def test_empty_result_set(self, requests_mock): requests_mock.get( f"{BASE_URL}ingestion/rules/save_result_set/", json={"results": [], "total": 0}, ) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) params = { "first_fetch": "4320", "retrieve_enriched_data": False, "integrationReliability": "C - Fairly reliable", } next_run, indicators = fetch_indicators(client, params, {}) assert len(indicators) == 0 def test_with_enrichment(self, requests_mock): mock_result_set = util_load_json("test_data/fetch_indicators_result_set.json") mock_enrichment = util_load_json("test_data/enrichment_bulk_lookup.json") lookup_url = re.compile(f"{BASE_URL}ingestion/openapi/bulk-lookup/.*") requests_mock.get(f"{BASE_URL}ingestion/rules/save_result_set/", json=mock_result_set) requests_mock.post(lookup_url, json=mock_enrichment) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) params = { "first_fetch": "4320", "retrieve_enriched_data": True, "integrationReliability": "C - Fairly reliable", } next_run, indicators = fetch_indicators(client, params, {}) assert len(indicators) == 4 # First indicator should have enrichment data merged ip_indicator = next((i for i in indicators if i["value"] == "1.2.3.4"), None) assert ip_indicator is not None class TestEnrichIndicatorsBulk: """Test enrich_indicators_bulk function.""" def test_basic_enrichment(self, requests_mock): mock_enrichment = util_load_json("test_data/enrichment_bulk_lookup.json") requests_mock.post(f"{BASE_URL}ingestion/openapi/bulk-lookup/indicator/", json=mock_enrichment) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) indicators = [ {"id": "ind-001-aaaa-bbbb-cccc-ddddeeee0001", "name": "1.2.3.4", "sdo_type": "indicator"}, {"id": "ind-002-aaaa-bbbb-cccc-ddddeeee0002", "name": "evil.example.com", "sdo_type": "indicator"}, ] enrichment_map = enrich_indicators_bulk(client, indicators) assert "ind-001-aaaa-bbbb-cccc-ddddeeee0001" in enrichment_map assert enrichment_map["ind-002-aaaa-bbbb-cccc-ddddeeee0002"]["name"] == "evil.example.com" assert enrichment_map["ind-001-aaaa-bbbb-cccc-ddddeeee0001"]["confidence_score"] == 90 def test_enrichment_paginates_second_page(self, requests_mock): """Bulk IOC lookup follows ``data.next`` (POST) and merges all result pages.""" page1 = { "next": "ingestion/openapi/bulk-lookup/indicator/?page=2&page_size=100", "previous": None, "total": 2, "results": [ { "id": "ind-001-aaaa-bbbb-cccc-ddddeeee0001", "name": "1.2.3.4", "object_type": "indicator", "confidence_score": 90, "relations": {}, } ], } page2 = { "next": None, "previous": None, "total": 2, "results": [ { "id": "ind-002-aaaa-bbbb-cccc-ddddeeee0002", "name": "evil.example.com", "object_type": "indicator", "confidence_score": 60, "relations": {}, } ], } requests_mock.post( re.compile(re.escape(BASE_URL) + r"ingestion/openapi/bulk-lookup/indicator/"), [{"json": page1}, {"json": page2}], ) client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) indicators = [ {"id": "ind-001-aaaa-bbbb-cccc-ddddeeee0001", "name": "1.2.3.4", "sdo_type": "indicator"}, {"id": "ind-002-aaaa-bbbb-cccc-ddddeeee0002", "name": "evil.example.com", "sdo_type": "indicator"}, ] enrichment_map = enrich_indicators_bulk(client, indicators) assert len(enrichment_map) == 2 assert enrichment_map["ind-001-aaaa-bbbb-cccc-ddddeeee0001"]["confidence_score"] == 90 assert enrichment_map["ind-002-aaaa-bbbb-cccc-ddddeeee0002"]["confidence_score"] == 60 first_req = requests_mock.request_history[0] second_req = requests_mock.request_history[1] first_qs = parse_qs(urlparse(first_req.url).query) second_qs = parse_qs(urlparse(second_req.url).query) assert first_qs.get("page") == ["1"] assert second_qs.get("page") == ["2"] assert first_qs.get("page_size") == ["100"] assert second_qs.get("page_size") == ["100"] first_body = json.loads(first_req.body.decode() if isinstance(first_req.body, bytes) else first_req.body) assert "object_id" in first_body assert "value" not in first_body @patch("CTIXv3.time.sleep", return_value=None) @patch("CTIXv3.demisto.error") def test_partial_failure(self, _mock_demisto_error, _mock_sleep): """Test that partial API failures are handled gracefully.""" client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) indicators = [ {"id": "ind-001-aaaa-bbbb-cccc-ddddeeee0001", "name": "1.2.3.4", "sdo_type": "indicator"}, ] # Mock at the client method level to avoid SystemExit from return_error with patch.object(client, "bulk_ioc_lookup_advanced", side_effect=DemistoException("API Error")): # Should not raise, returns empty map on failure (after execute_with_retry retry path) enrichment_map = enrich_indicators_bulk(client, indicators) assert len(enrichment_map) == 0 def test_empty_indicators(self, requests_mock): client = Client( base_url=BASE_URL, access_id=ACCESS_ID, secret_key=SECRET_KEY, verify=False, timeout=15, proxies={}, ) enrichment_map = enrich_indicators_bulk(client, []) assert len(enrichment_map) == 0 class TestRetryLogic: """Test execute_with_retry helper (retries only when the error indicates HTTP 429).""" def setup_method(self): """Reset global RETRY_COUNT before each test.""" import CTIXv3 CTIXv3.RETRY_COUNT = 0 def test_execute_with_retry_success(self): """Test success on first try.""" mock_func = MagicMock(return_value="success") result = execute_with_retry(mock_func, "arg1", kwarg1="val1") assert result == "success" mock_func.assert_called_once_with("arg1", kwarg1="val1") @patch("CTIXv3.time.sleep", return_value=None) @patch("CTIXv3.demisto.error") def test_execute_with_retry_after_one_failure(self, _mock_demisto_error, mock_sleep): """Test success after one rate-limit (429) failure and a single retry.""" mock_func = MagicMock(side_effect=[DemistoException("status-> 429"), "success"]) result = execute_with_retry(mock_func, "arg1") assert result == "success" assert mock_func.call_count == 2 mock_sleep.assert_called_once_with(60) @patch("CTIXv3.time.sleep", return_value=None) @patch("CTIXv3.demisto.error") def test_execute_with_retry_persistent_failure(self, _mock_demisto_error, mock_sleep): """After a 429 retry, a non-429 error from the retried call propagates (per-call single retry).""" mock_func = MagicMock(side_effect=[DemistoException("status-> 429"), DemistoException("Second failure")]) with pytest.raises(DemistoException, match="Second failure"): execute_with_retry(mock_func, "arg1") assert mock_func.call_count == 2 mock_sleep.assert_called_once_with(60) @patch("CTIXv3.time.sleep", return_value=None) @patch("CTIXv3.demisto.error") def test_execute_with_retry_global_limit(self, _mock_demisto_error, mock_sleep): """Global RETRY_COUNT limits how many 429 retries are allowed across execute_with_retry calls.""" mock_func = MagicMock(side_effect=DemistoException("status-> 429")) import CTIXv3 # Each execute_with_retry: first 429 increments RETRY_COUNT and retries once; second 429 is not caught. with pytest.raises(DemistoException): execute_with_retry(mock_func) assert CTIXv3.RETRY_COUNT == 1 with pytest.raises(DemistoException): execute_with_retry(mock_func) assert CTIXv3.RETRY_COUNT == 2 with pytest.raises(DemistoException): execute_with_retry(mock_func) assert CTIXv3.RETRY_COUNT == 3 # Fourth top-level call: increment makes RETRY_COUNT > 3, so raise without sleep/retry. with pytest.raises(DemistoException): execute_with_retry(mock_func) assert CTIXv3.RETRY_COUNT == 4 assert mock_sleep.call_count == 3 # Only first three top-level calls slept before retry class TestFetchIncidentsRateLimit: """Test fetch_incidents with Rate Limit handling.""" def setup_method(self): """Reset global RETRY_COUNT before each test.""" import CTIXv3 CTIXv3.RETRY_COUNT = 0 @patch("CTIXv3.demisto.error") @patch("CTIXv3.time.sleep", return_value=None) @patch("CTIXv3.execute_with_retry") def test_fetch_incidents_rate_limit_initial_call(self, mock_execute, mock_sleep, mock_demisto_error, requests_mock): """Test that fetch_incidents returns partial results if initial call fails after retry.""" client = Client(BASE_URL, ACCESS_ID, SECRET_KEY, 15, False, {}) params = {"first_fetch": "4320"} last_run = {} # Mock initial get_threat_data failing twice with 429 mock_execute.side_effect = DemistoException("status-> 429") next_run, incidents = fetch_incidents(client, params, last_run) assert len(incidents) == 0 assert next_run.get("page_number") == 1 mock_demisto_error.assert_called() @patch("CTIXv3.demisto.error") @patch("CTIXv3.time.sleep", return_value=None) def test_fetch_incidents_rate_limit_during_loop(self, mock_sleep, mock_demisto_error, requests_mock): """Rate limit on relation enrichment: still return incidents from base fetch; partial LastRun if next page exists.""" full = util_load_json("test_data/fetch_incidents_reports.json") mock_response = { **full, "results": full["results"][:1], "next": "https://example.com/ctixapi/ingestion/threat-data/list/?page=2&page_size=100", } requests_mock.post(f"{BASE_URL}ingestion/threat-data/list/", json=mock_response) client = Client(BASE_URL, ACCESS_ID, SECRET_KEY, 15, False, {}) from CTIXv3 import execute_with_retry as real_execute_with_retry def side_effect(func, *args, **kwargs): if func.__name__ == "bulk_ioc_lookup_advanced": raise DemistoException("status-> 429") return real_execute_with_retry(func, *args, **kwargs) with patch("CTIXv3.execute_with_retry", side_effect=side_effect): params = {"first_fetch": "4320"} next_run, incidents = fetch_incidents(client, params, {}) assert len(incidents) == 1 assert incidents[0]["name"] == "CTIX Intel: APT29 Campaign Analysis" assert next_run.get("page_number") == 2 # Partial checkpoint preserves sweep lower bound, not completion time.time() watermark assert next_run.get("last_fetch_time") == next_run.get("last_run_date") assert abs(next_run["last_fetch_time"] - int(datetime.now(UTC).timestamp())) > 60 error_messages = [str(call[0][0]) for call in mock_demisto_error.call_args_list] assert any("Rate limit hit again" in m for m in error_messages) assert any("Relation enrichment failed" in m for m in error_messages)