FeedCyCognito

The CyCognito Feed integration retrieves the discovered assets from the CyCognito platform based on user-specified filters. A comprehensive dashboard and layout are also included.

Data Enrichment & Threat Intelligence · CyCognito Feed · Feed

Details

IDFeedCyCognito
ProviderCyCognito
CategoryData Enrichment & Threat Intelligence
From Version6.2.0
Docker Imagedemisto/pycountry:1.0.0.10120494
Supported ModulesAgentix XSIAM

README

The CyCognito Feed integration retrieves the discovered assets from the CyCognito platform based on user-specified filters. A comprehensive dashboard and layout are also included.
This integration was integrated and tested with CyCognito V1 API.

Configure CyCognito Feed in Cortex

  1. Navigate to Settings > Integrations > Servers & Services.
  2. Search for CyCognito Feed.
  3. Click Add instance to create and configure a new integration instance.
Parameter Description Required
API Key The API Key required to authenticate to the service. True
Region Select the region based on the location of the CyCognito instance. If the region is not listed in the dropdown, choose the “Other” option and specify the region in the “Other Region” text field. False
Other Region Specify the region based on the location of the CyCognito instance. Only applicable if the “Other” option is selected in the Region dropdown. False
Asset Type The type of assets to be retrieved. True
First Fetch Time The date or relative timestamp from where to start fetching assets.

Supported formats: 2 minutes, 2 hours, 2 days, 2 weeks, 2 months, 2 years, yyyy-mm-dd, yyyy-mm-ddTHH:MM:SSZ

For example: 01 Mar 2021, 01 Feb 2021 04:45:33, 2022-04-17T14:05:44Z
False
Max Fetch The maximum number of assets to fetch every time. The maximum value is ‘1000’. False
Organizations Retrieves the assets attributed to a specific organization, subsidiary, or assignee. Supports comma-separated values. False
Security Grade Filters the assets according to security grade.

Supported values:
A: Very strong,
B: Strong,
C: Less vulnerable,
D: Vulnerable,
F: Highly vulnerable
False
Hosting Type Filters the assets based on their hosting type.

Supported values: ‘owned’, ‘cloud’, ‘undetermined’
False
Locations Filters the assets based on the geographic locations to which they belong. Multiple selection is supported.

Locations are available only for IP, Domain, and Certificate asset types.
False
Fetch indicators Indicates whether to fetch indicators from the instance. False
Indicator Reputation Indicators from this integration instance will be marked with this reputation. False
Source Reliability Reliability of the source providing the intelligence data. True
Feed Fetch Interval Time interval for fetching indicators. 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 Supports CSV values. False
Traffic Light Protocol Color The Traffic Light Protocol (TLP) designation to apply to indicators fetched from the feed False
Default Indicator Mapping When selected, all the incoming indicators will map to a CyCognito Asset indicator type. False
Fetch only Live Assets When selected, Fetches only live assets.

Note: This filter supports only IP and IP Range type of assets.
False
Trust any certificate (not secure) Indicates whether to allow connections without verifying the SSL certificate’s validity. False
Use system proxy settings Indicates whether to use XSOAR’s system proxy settings to connect to the API. False
  1. Click Test to validate the URLs, token, and connection.

Commands

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

cycognito-get-indicators


Fetches a given limit of indicators from the CyCognito platform and displays them in human-readable format in the war room.

Base Command

cycognito-get-indicators

Input

Argument Name Description Required
asset_type The type of asset to be retrieved.

Supported values: ‘ip’, ‘domain’, ‘cert’, ‘webapp’, ‘iprange’
Required
count The number of results to retrieve.

Maximum value is ‘1000’

Default value is ‘50’
Optional
offset Sets the starting index for the returned results. By specifying offset, you retrieve a subset of records starting with the offset value.

Default value is ‘0’
Optional
search An Advanced Search parameter to query the response.

Note: Retrieves all the occurrences that are included in the string.
Optional
sort_by The name of the field by which to sort the results. The response fields available for sorting the data are found in the following documentation: https://docs.cycognito.com/reference/reference-getting-started Optional
sort_order Specifies whether to sort the results in either ascending or descending order.

Supported values: ‘asc’, ‘desc’

Default value is ‘desc’
Optional
first_seen The date and time at which CyCognito first discovered and attributed the asset to the organization.

Supported formats: 2 minutes, 2 hours, 2 days, 2 weeks, 2 months, 2 years, yyyy-mm-dd, yyyy-mm-ddTHH:MM:SSZ

For example: 01 Mar 2021, 01 Feb 2021 04:45:33, 2022-04-17T14:05:44Z
Optional
last_seen The date and time at which CyCognito most recently attributed the asset to the organization.

Supported formats: 2 minutes, 2 hours, 2 days, 2 weeks, 2 months, 2 years, yyyy-mm-dd, yyyy-mm-ddTHH:MM:SSZ

For example: 01 Mar 2021, 01 Feb 2021 04:45:33, 2022-04-17T14:05:44Z
Optional
organizations Retrieves the assets attributed to a specific organization, subsidiary, or assignee. Supports comma-separated values. Optional
hosting_type Filters the assets according to their hosting type. Supports comma-separated values.

Supported values: ‘owned’, ‘cloud’, ‘undetermined’
Optional
security_grade Filters the assets according to security rating. Supports comma-separated values.

Supported values: ‘A’, ‘B’, ‘C’, ‘D’, ‘F’

Where:
A = Very strong
B = Strong
C = Less vulnerable
D = Vulnerable
F = Highly vulnerable
Optional
status Filters the assets according to their status. Supports comma-separated values.

Supported values: ‘changed’, ‘new’, ‘normal’
Optional
locations The geographical locations in which the asset is found. Supported values contain the three-letter ISO country code for the respective countries–e.g., IND, USA.

Locations are available only for IP, Domain, and Certificate asset types.
Optional
tags A keyword or phrase that can be added to an asset or issue metadata. Supports comma-separated values. Optional

Context Output

There is no context output for this command.

Command example

!cycognito-get-indicators asset_type=ip count=2

Human Readable Output

Indicator Detail

Asset type: IP

Asset ID Security Grade Status Organizations First Seen Last Seen Locations Hosting Type
127.0.0.1 F normal ACME Ticketing, ACME Cleantech Solutions, Acme Holdings 30 Jun 2021, 12:32 PM 24 Mar 2022, 04:26 AM United States cloud
127.0.0.2 D normal Acme Interior Design 22 Jul 2021, 03:07 AM 24 Mar 2022, 04:26 AM India owned

Configuration parameters

  • feedIncremental — Incremental Feed
  • api_key — API Key (required)
  • region — Region
  • other_region — Other Region
  • asset_type — Asset Type (required)
  • first_fetch — First Fetch Time
  • max_fetch — Max Fetch
  • organizations — Organizations
  • security_grade — Security Grade
  • hosting_type — Hosting Type
  • locations — Locations
  • feed — Fetch indicators
  • feedReputation — Indicator Reputation
  • feedReliability — Source Reliability (required)
  • feedFetchInterval — Feed Fetch Interval
  • feedExpirationPolicy
  • feedExpirationInterval
  • feedBypassExclusionList — Bypass exclusion list
  • feedTags — Tags
  • tlp_color — Traffic Light Protocol Color
  • default_mapping — Default Indicator Mapping
  • only_alive — Fetch only Live Assets
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings

Commands (1)

  • cycognito-get-indicators

    Fetches a given limit of indicators from the CyCognito platform and displays them in human-readable format in the war room.

"""Test file for FeedCyCognito Integration."""

import json
import os
import time

import pytest

from FeedCyCognito import (
    AVAILABLE_ASSET_TYPES,
    AVAILABLE_HOSTING_TYPES,
    AVAILABLE_STATUS_TYPES,
    AVAILABLE_SECURITY_GRADE,
    BASE_URL,
    DATE_FORMAT,
    ERRORS,
)
from CommonServerPython import arg_to_datetime

DUMMY_TIME = "2022-03-21T07:06:41.000Z"
ASSET_IP_ENDPOINT = "/assets/ip"
CURRENT_TIME = time.time()
BASE_URL = BASE_URL.format("")


def util_load_json(path):
    """Load a json file to python dictionary."""
    with open(path, encoding="utf-8") as f:
        return json.loads(f.read())


@pytest.fixture()
def mocked_client():
    """Create a mock client for testing."""
    from FeedCyCognito import CyCognitoFeedClient

    client = CyCognitoFeedClient(params={"api_key": "dummy_key"}, verify=False, proxy=False)
    return client


@pytest.mark.parametrize(
    "expected, args",
    [
        (
            [{"field": "locations", "op": "in", "values": ["IND"]}, {"field": "hosting-type", "op": "in", "values": ["cloud"]}],
            {"asset_type": "ip", "hosting_type": ["cloud"], "locations": ["IND"]},
        ),
        (
            [{"field": "locations", "op": "in", "values": ["IND"]}, {"field": "hosting-type", "op": "in", "values": ["cloud"]}],
            {"asset_type": "domain", "hosting_type": ["cloud"], "locations": ["IND"]},
        ),
        ([{"field": "security-grade", "op": "in", "values": ["a"]}], {"asset_type": "iprange", "security_grade": ["a"]}),
        (
            [
                {
                    "field": "first-seen",
                    "op": "between",
                    "values": [["2020-01-01T00:00:00Z", arg_to_datetime(CURRENT_TIME).strftime(DATE_FORMAT)]],
                },
                {
                    "field": "last-seen",
                    "op": "between",
                    "values": [["2020-01-01T00:00:00Z", arg_to_datetime(CURRENT_TIME).strftime(DATE_FORMAT)]],
                },
            ],
            {"first_seen": "2020-01-01T00:00:00Z", "last_seen": "2020-01-01T00:00:00Z"},
        ),
    ],
)
def test_prepare_filters_for_get_indicators(expected, args, mocker):
    """Test case scenario for successful execution of prepare_filters_for_get_indicators function."""
    from FeedCyCognito import prepare_body_filters_for_get_indicators

    mocker.patch("time.time", return_value=CURRENT_TIME)
    assert prepare_body_filters_for_get_indicators(**args) == expected


@pytest.mark.parametrize(
    "err_msg, args",
    [
        (ERRORS["INVALID_REQUIRED_PARAMETER"].format("asset_type"), {"asset_type": ""}),
        (ERRORS["INVALID_SINGLE_SELECT_PARAM"].format("test", "asset_type", AVAILABLE_ASSET_TYPES), {"asset_type": "test"}),
        (ERRORS["INVALID_PAGE_SIZE"].format("-10"), {"count": "-10", "asset_type": "ip"}),
        ('Invalid number: "count"="abc"', {"count": "abc"}),
        (ERRORS["INVALID_PAGE_SIZE"].format("10000"), {"count": "10000", "asset_type": "ip"}),
        ('Invalid number: "offset"="abc"', {"offset": "abc"}),
        ('Invalid date: "first_seen"="abc"', {"first_seen": "abc"}),
        ('Invalid date: "last_seen"="abc"', {"last_seen": "abc"}),
        (
            ERRORS["INVALID_SINGLE_SELECT_PARAM"].format("test", "sort_order", ["asc", "desc"]),
            {"sort_order": "test", "asset_type": "ip"},
        ),
        (
            ERRORS["INVALID_MULTI_SELECT_PARAM"].format("hosting_type", AVAILABLE_HOSTING_TYPES),
            {"hosting_type": "test", "asset_type": "ip"},
        ),
        (
            ERRORS["INVALID_MULTI_SELECT_PARAM"].format("security_grade", [x.upper() for x in AVAILABLE_SECURITY_GRADE]),
            {"security_grade": "a,e", "asset_type": "ip"},
        ),
        (ERRORS["INVALID_MULTI_SELECT_PARAM"].format("status", AVAILABLE_STATUS_TYPES), {"status": "test", "asset_type": "ip"}),
    ],
)
def test_get_indicators_arguments_when_invalid_arguments_provided(err_msg, args, mocked_client):
    """Test case scenario when arguments provided to get_indicators_command are invalid."""
    from FeedCyCognito import get_indicators_command

    with pytest.raises(ValueError) as err:
        get_indicators_command(mocked_client, args)

    assert str(err.value) == err_msg


def test_get_indicators_command_when_valid_response_returned_for_ip(requests_mock, mocked_client):
    """Test case scenario for successful execution of cycognito-get-indicators command."""
    from FeedCyCognito import get_indicators_command

    expected_resp = util_load_json(
        os.path.join(os.path.dirname(os.path.realpath(__file__)), "test_data/get_indicators_success_response_ip.json")
    )
    with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), "test_data/get_indicators_success_hr_ip.md")) as f:
        expected_hr_output = f.read()

    requests_mock.post(BASE_URL + ASSET_IP_ENDPOINT, json=expected_resp)
    resp = get_indicators_command(mocked_client, {"asset_type": "ip"})
    assert resp.raw_response == expected_resp
    assert resp.readable_output == expected_hr_output


def test_get_indicators_command_when_valid_response_returned_for_domain(requests_mock, mocked_client):
    """Test case scenario for successful execution of cycognito-get-indicators command."""
    from FeedCyCognito import get_indicators_command

    expected_resp = util_load_json(
        os.path.join(os.path.dirname(os.path.realpath(__file__)), "test_data/get_indicators_success_response_domain.json")
    )
    with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), "test_data/get_indicators_success_hr_domain.md")) as f:
        expected_hr_output = f.read()

    requests_mock.post(BASE_URL + "/assets/domain", json=expected_resp)
    resp = get_indicators_command(mocked_client, {"asset_type": "domain"})
    assert resp.raw_response == expected_resp
    assert resp.readable_output == expected_hr_output


def test_get_indicators_command_when_valid_response_returned_for_cert(requests_mock, mocked_client):
    """Test case scenario for successful execution of cycognito-get-indicators command."""
    from FeedCyCognito import get_indicators_command

    expected_resp = util_load_json(
        os.path.join(os.path.dirname(os.path.realpath(__file__)), "test_data/get_indicators_success_response_cert.json")
    )
    with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), "test_data/get_indicators_success_hr_cert.md")) as f:
        expected_hr_output = f.read()

    requests_mock.post(BASE_URL + "/assets/cert", json=expected_resp)
    resp = get_indicators_command(mocked_client, {"asset_type": "cert"})
    assert resp.raw_response == expected_resp
    assert resp.readable_output == expected_hr_output


def test_get_indicators_command_when_valid_response_returned_for_iprange(requests_mock, mocked_client):
    """Test case scenario for successful execution of cycognito-get-indicators command."""
    from FeedCyCognito import get_indicators_command

    expected_resp = util_load_json(
        os.path.join(os.path.dirname(os.path.realpath(__file__)), "test_data/get_indicators_success_response_iprange.json")
    )
    with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), "test_data/get_indicators_success_hr_iprange.md")) as f:
        expected_hr_output = f.read()

    requests_mock.post(BASE_URL + "/assets/iprange", json=expected_resp)
    resp = get_indicators_command(mocked_client, {"asset_type": "iprange"})
    assert resp.raw_response == expected_resp
    assert resp.readable_output == expected_hr_output


def test_get_indicators_command_when_empty_response_returned(requests_mock, mocked_client):
    """Test case scenario for successful execution of cycognito-get-indicators command when empty response returned."""
    from FeedCyCognito import get_indicators_command

    requests_mock.post(BASE_URL + ASSET_IP_ENDPOINT, json=[])
    resp = get_indicators_command(mocked_client, {"asset_type": "ip"})

    assert resp.raw_response == []
    assert resp.readable_output == "### Indicator Detail: \n #### Asset type: IP\n**No entries.**\n"


def test_test_module(requests_mock, mocked_client):
    """Test case scenario for successful execution of test-module command when valid response returned."""
    from FeedCyCognito import test_module

    requests_mock.post(f"{BASE_URL}{ASSET_IP_ENDPOINT}", json=[], status_code=200)
    assert test_module(mocked_client, {"feed": False}) == "ok"


@pytest.mark.parametrize(
    "err_msg, args",
    [
        (ERRORS["INVALID_PAGE_SIZE"].format("-10"), {"max_fetch": "-10", "asset_type": "ip", "feed": False}),
        ('Invalid number: "Max Fetch"="abc"', {"max_fetch": "abc", "feed": False}),
        (ERRORS["INVALID_PAGE_SIZE"].format("10000"), {"max_fetch": "10000", "asset_type": "ip", "feed": False}),
        ('Invalid date: "First Fetch Time"="abc"', {"first_fetch": "abc", "feed": False}),
        (
            ERRORS["INVALID_MULTI_SELECT_PARAM"].format("hosting_type", AVAILABLE_HOSTING_TYPES),
            {"hosting_type": "test", "asset_type": "ip", "feed": False},
        ),
        (
            ERRORS["INVALID_MULTI_SELECT_PARAM"].format("security_grade", [x.upper() for x in AVAILABLE_SECURITY_GRADE]),
            {"security_grade": "a,e", "asset_type": "ip", "feed": False},
        ),
        (
            ERRORS["INVALID_COUNTRY_ERROR"].format("invalid_country_name"),
            {"locations": ["invalid_country_name"], "asset_type": "ip", "feed": False},
        ),
    ],
)
def test_fetch_indicators_when_invalid_arguments_provided(err_msg, args, mocked_client, capfd):
    """Test case scenario when arguments provided to fetch-indicators are invalid."""
    from FeedCyCognito import fetch_indicators_command

    with pytest.raises(ValueError) as err:
        capfd.close()
        fetch_indicators_command(mocked_client, args, {})

    assert str(err.value) == err_msg


def test_fetch_indicators_command_when_valid_response_returned_with_updated_last_run(requests_mock, mocked_client):
    """Test case scenario when valid response returned by fetch-indicators command with updated last run."""
    from FeedCyCognito import fetch_indicators_command

    mock_response = util_load_json(
        os.path.join(os.path.dirname(os.path.realpath(__file__)), "test_data/fetch_indicator_response.json")
    )

    indicators = util_load_json(
        os.path.join(os.path.dirname(os.path.realpath(__file__)), "test_data/fetch_indicator_indicators.json")
    )

    requests_mock.post(BASE_URL + ASSET_IP_ENDPOINT, json=mock_response, status_code=200)

    args = {
        "asset_type": "ip",
        "max_fetch": 10,
        "feed": True,
        "first_fetch": "10 days",
        "organizations": "Acme Holding, Acme Interior",
        "security_grade": ["A: Very Strong", "B: Strong"],
        "hosting_type": ["owned"],
        "locations": ["India", "United States"],
        "default_mapping": False,
    }
    last_run = {"last_fetch": DUMMY_TIME, "offset": 2}
    next_run, actual_indicators = fetch_indicators_command(mocked_client, args, last_run)

    assert next_run == {"last_fetch": "2022-03-31T03:39:22.569000Z", "offset": 0}
    assert actual_indicators == indicators


def test_fetch_indicators_command_when_empty_response_returned_with_last_run(requests_mock, mocked_client):
    """Test case scenario when empty response is returned with same last run as previous."""
    from FeedCyCognito import fetch_indicators_command

    requests_mock.post(BASE_URL + ASSET_IP_ENDPOINT, json=[], status_code=200)

    args = {"asset_type": "ip", "max_fetch": 10, "feed": True, "locations": ["India"]}
    last_run = {"last_fetch": DUMMY_TIME, "offset": 2}
    next_run, actual_indicators = fetch_indicators_command(mocked_client, args, last_run)

    assert next_run == {"last_fetch": DUMMY_TIME, "offset": 2}
    assert actual_indicators == []


def test_fetch_indicators_command_when_empty_response_returned_without_last_run(requests_mock, mocked_client):
    """Test case scenario when empty response is returned with empty last run."""
    from FeedCyCognito import fetch_indicators_command

    requests_mock.post(BASE_URL + ASSET_IP_ENDPOINT, json=[], status_code=200)

    args = {"asset_type": "ip", "max_fetch": 10, "feed": True, "locations": ["India"]}
    next_run, actual_indicators = fetch_indicators_command(mocked_client, args, {})

    assert next_run == {}
    assert actual_indicators == []


def test_test_module_when_region_param_is_selected(requests_mock):
    """
    Test case scenario for successful execution of test_module when region param is selected.

    Given:
       - mocked client
    When:
       - Calling `test_module` function
    Then:
       - Returns an ok message
    """
    from FeedCyCognito import test_module, CyCognitoFeedClient, BASE_URL

    params = {"api_key": "dummy_key", "region": "US", "feed": "false"}
    BASE_URL = BASE_URL.format("us-")

    mock_client = CyCognitoFeedClient(params=params, verify=False, proxy=False)

    requests_mock.post(f"{BASE_URL}/assets/ip", json=[], status_code=200)

    assert test_module(mock_client, params) == "ok"


def test_test_module_when_other_region_param_is_selected(requests_mock):
    """
    Test case scenario for successful execution of test_module when other_region param is selected.

    Given:
       - mocked client
    When:
       - Calling `test_module` function
    Then:
       - Returns an ok message
    """
    from FeedCyCognito import test_module, CyCognitoFeedClient, BASE_URL

    params = {"api_key": "dummy_key", "region": "Other", "other_region": "europe", "feed": "false"}
    BASE_URL = BASE_URL.format("europe-")

    mock_client = CyCognitoFeedClient(params=params, verify=False, proxy=False)

    requests_mock.post(f"{BASE_URL}/assets/ip", json=[], status_code=200)

    assert test_module(mock_client, params) == "ok"