Infoblox BloxOne Threat Defense Event Collector

BloxOne Threat Defense is a hybrid cybersecurity solution that leverages DNS as the first line of defense to detect and block cyber threats.

Data Enrichment & Threat Intelligence · Infoblox Threat Defense with DDI

Details

IDInfoblox BloxOne Threat Defense Event Collector
ProviderInfoblox
CategoryData Enrichment & Threat Intelligence
From Version6.10.0
Docker Imagedemisto/python3:3.12.13.10116658
Supported ModulesAgentix XSIAM

README

BloxOne Threat Defense is a hybrid cybersecurity solution that leverages DNS as the first line of defense to detect and block cyber threats.

Configure Infoblox BloxOne Threat Defense Event Collector in Cortex

Parameter Description Required
Service API Key   True
First fetch time interval   False
Max events per fetch The maximum amount of events to retrieve for each event type (up to 10000 events). For more information about event types see the help section. False
Trust any certificate (not secure)   False
Use system proxy settings   False

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.

bloxone-td-event-collector-get-events


Gets events. This command is for debugging purposes.

Base Command

bloxone-td-event-collector-get-events

Input

Argument Name Description Required
should_push_events Set this argument to True in order to create events, otherwise the command will only display them. Possible values are: True, False. Default is False. Required
from Timestamp indicating when to start fetching events. Required
to Timestamp indicating when to stop fetching events. Required
limit Maximum number of events to fetch. Default is 1000. Required
offset offset of the events. Required

Context Output

Path Type Description
TestGetEvents Unknown The event data.

Configuration parameters

  • credentials — (required)
  • first_fetch — First fetch time interval
  • max_fetch — Max events per fetch
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings

Commands (1)

  • bloxone-td-event-collector-get-events

    Gets events. This command is for debugging purposes.

import pytest
from freezegun import freeze_time
from InfobloxBloxOneThreatDefenseEventCollector import *


class TestBloxOneTDEventCollectorClient:
    def test_fetch_url(self, requests_mock):
        request_mock_get = requests_mock.get("/api/dnsdata/v2/dns_event", json={"result": []})
        BloxOneTDEventCollectorClient("").fetch_events(0, 0)
        assert request_mock_get.called_once

    def test_event_time_mapping(self, requests_mock):
        event_time = "the event time"
        requests_mock.get("/api/dnsdata/v2/dns_event", json={"result": [{"event_time": event_time}]})
        event = BloxOneTDEventCollectorClient("").fetch_events(0, 0)[0]
        assert event["event_time"] == event["_time"] == event_time


class TestFetchEventsCommand:
    data_test_fetch_events_command = [
        ({"from_ts": 1680307140}, {"from_ts": 1680307200}, 3),
        ({"from_ts": 1680307140}, {"from_ts": 1680307140, "offset": 5}, 5),
        ({"from_ts": 1680307140, "offset": 5}, {"from_ts": 1680307140, "offset": 10}, 5),
        ({"from_ts": 1680307140, "offset": 10}, {"from_ts": 1680307200}, 2),
    ]

    @freeze_time("2023-04-01T00:00:00.000Z")
    @pytest.mark.parametrize("last_run, expected_next_run, events_length", data_test_fetch_events_command)
    def test_fetch_events_command(self, last_run, expected_next_run, events_length, requests_mock, mocker):
        requests_mock.get("/api/dnsdata/v2/dns_event", json={"result": [{"event_time": "event_time"}] * events_length})
        set_last_run = mocker.patch.object(demisto, "setLastRun")
        send_events_to_xsiam_mock = mocker.patch("InfobloxBloxOneThreatDefenseEventCollector.send_events_to_xsiam")
        fetch_events_command(BloxOneTDEventCollectorClient(""), {"max_fetch": 5}, last_run)
        set_last_run.assert_called_once_with(expected_next_run)
        send_events_to_xsiam_mock.assert_called_once_with(
            [{"_time": "event_time", "event_time": "event_time"}] * events_length, "Infoblox BloxOne", "Threat Defense"
        )

    @freeze_time("2023-04-01T00:00:00.000Z")
    def test_fetch_events_first_fetch(self, requests_mock, mocker):
        get_events_api_call = requests_mock.get("/api/dnsdata/v2/dns_event", json={"result": [{"event_time": "event_time"}]})
        send_events_to_xsiam_mock = mocker.patch("InfobloxBloxOneThreatDefenseEventCollector.send_events_to_xsiam")
        fetch_events_command(BloxOneTDEventCollectorClient(""), {"max_fetch": 5, "first_fetch": "5 min"}, {})
        send_events_to_xsiam_mock.assert_called_once_with(
            [{"_time": "event_time", "event_time": "event_time"}], "Infoblox BloxOne", "Threat Defense"
        )
        assert get_events_api_call.last_request.qs["t0"][0] == "1680306900"

    def test_fetch_events_with_invalid_first_fetch(self, requests_mock, mocker):
        with pytest.raises(DemistoException):
            fetch_events_command(
                BloxOneTDEventCollectorClient(""), {"first_fetch": "this is a test and it should raise an error"}, {}
            )


class TestGetEventsCommand:
    def test_get_events_command_without_send_to_xsiam(self, requests_mock, mocker):
        send_events_to_xsiam_mock = mocker.patch("InfobloxBloxOneThreatDefenseEventCollector.send_events_to_xsiam")
        get_events_api_call = requests_mock.get("/api/dnsdata/v2/dns_event", json={"result": [{"event_time": "event_time"}]})
        get_events_command(BloxOneTDEventCollectorClient(""), {"from": 1, "to": 2})
        assert get_events_api_call.called_once
        assert get_events_api_call.last_request.qs["t0"][0] == "1"
        assert get_events_api_call.last_request.qs["t1"][0] == "2"
        assert send_events_to_xsiam_mock.call_count == 0

    def test_get_events_command_and_send_to_xsiam(self, requests_mock, mocker):
        send_events_to_xsiam_mock = mocker.patch("InfobloxBloxOneThreatDefenseEventCollector.send_events_to_xsiam")
        get_events_api_call = requests_mock.get("/api/dnsdata/v2/dns_event", json={"result": [{"event_time": "event_time"}]})
        get_events_command(BloxOneTDEventCollectorClient(""), {"from": 1, "to": 2, "should_push_events": "yes"})
        assert get_events_api_call.called_once
        assert get_events_api_call.last_request.qs["t0"][0] == "1"
        assert get_events_api_call.last_request.qs["t1"][0] == "2"
        send_events_to_xsiam_mock.assert_called_once_with(
            [{"_time": "event_time", "event_time": "event_time"}], "Infoblox BloxOne", "Threat Defense"
        )


class TestParseFromTsFromParams:
    data_test_parse_from_ts_from_params_with_valid_params = [
        (None, 1680220800),
        ("5 min", 1680306900),
    ]

    @freeze_time("2023-04-01T00:00:00.000Z")
    @pytest.mark.parametrize("first_fetch, expected_from_ts", data_test_parse_from_ts_from_params_with_valid_params)
    def test_parse_from_ts_from_params_with_valid_params(self, first_fetch, expected_from_ts):
        assert parse_from_ts_from_params(first_fetch) == expected_from_ts

    def test_parse_from_ts_from_params_with_invalid_params(self):
        with pytest.raises(DemistoException):
            parse_from_ts_from_params("this is a test and it should raise an error")


class TestCommandTestModule:
    def test_default_flow(self, requests_mock):
        requests_mock.get("/api/dnsdata/v2/dns_event", json={"result": [{"event_time": "event_time"}]})
        assert command_test_module(BloxOneTDEventCollectorClient(""), {}) == "ok"

    def test_valid_first_fetch(self, requests_mock):
        requests_mock.get("/api/dnsdata/v2/dns_event", json={"result": [{"event_time": "event_time"}]})
        assert command_test_module(BloxOneTDEventCollectorClient(""), {"first_fetch": "5 min"}) == "ok"

    def test_invalid_first_fetch(self, requests_mock):
        requests_mock.get("/api/dnsdata/v2/dns_event", json={"result": [{"event_time": "event_time"}]})
        with pytest.raises(DemistoException):
            command_test_module(BloxOneTDEventCollectorClient(""), {"first_fetch": "invalid"})

    def test_invalid_creds(self, requests_mock):
        requests_mock.get("/api/dnsdata/v2/dns_event", status_code=401, json={"i dont": "really care"})
        with pytest.raises(DemistoException):
            command_test_module(BloxOneTDEventCollectorClient(""), {"first_fetch": "invalid"})


def demisto_input_mocker(mocker, command, params={}, args={}):
    mocker.patch.object(demisto, "command", return_value=command)
    mocker.patch.object(demisto, "params", return_value=params)
    mocker.patch.object(demisto, "args", return_value=args)


@pytest.fixture()
def return_error_mock(mocker):
    return mocker.patch("InfobloxBloxOneThreatDefenseEventCollector.return_error")


@pytest.fixture()
def return_results_mock(mocker):
    return mocker.patch("InfobloxBloxOneThreatDefenseEventCollector.return_results")


class TestMain:
    def test_command_not_implemented(self, mocker, return_error_mock):
        demisto_input_mocker(mocker, "not_implemented")
        main()
        return_error_mock.assert_called_once()
        assert "command not_implemented is not implemented." in return_error_mock.call_args.args[0]

    def test_unauthenticated(self, mocker, return_error_mock, requests_mock):
        demisto_input_mocker(mocker, "test-module")
        requests_mock.get("/api/dnsdata/v2/dns_event", status_code=401, json={"i dont": "really care"})
        main()
        return_error_mock.assert_called_once()
        assert return_error_mock.call_args.args[0] == "authentication error please check your API key and try again."

    def test_test_module_happy_flow(self, mocker, return_results_mock, requests_mock):
        demisto_input_mocker(mocker, "test-module")
        requests_mock.get("/api/dnsdata/v2/dns_event", json={"result": []})
        main()
        return_results_mock.assert_called_once_with("ok")

    def test_fetch_events_happy_flow(self, mocker, requests_mock):
        demisto_input_mocker(mocker, "fetch-events")
        requests_mock.get("/api/dnsdata/v2/dns_event", json={"result": [{"event_time": "event_time"}]})
        send_events_to_xsiam_mock = mocker.patch("InfobloxBloxOneThreatDefenseEventCollector.send_events_to_xsiam")
        main()
        send_events_to_xsiam_mock.assert_called_once_with(
            [{"_time": "event_time", "event_time": "event_time"}], "Infoblox BloxOne", "Threat Defense"
        )

    def test_get_events_and_send_to_server_happy_flow(self, mocker, requests_mock, return_results_mock):
        demisto_input_mocker(
            mocker, "bloxone-td-event-collector-get-events", args={"from": "from", "to": "to", "should_push_events": "yes"}
        )
        requests_mock.get("/api/dnsdata/v2/dns_event", json={"result": [{"event_time": "event_time"}]})
        send_events_to_xsiam_mock = mocker.patch("InfobloxBloxOneThreatDefenseEventCollector.send_events_to_xsiam")
        main()
        send_events_to_xsiam_mock.assert_called_once_with(
            [{"_time": "event_time", "event_time": "event_time"}], "Infoblox BloxOne", "Threat Defense"
        )
        assert return_results_mock.call_args.args[0].outputs == [{"_time": "event_time", "event_time": "event_time"}]
        assert return_results_mock.call_args.args[0].outputs_prefix == "TestGetEvents"

    def test_get_events_without_send_to_server_happy_flow(self, mocker, requests_mock, return_results_mock):
        demisto_input_mocker(
            mocker, "bloxone-td-event-collector-get-events", args={"from": "from", "to": "to", "should_push_events": "no"}
        )
        requests_mock.get("/api/dnsdata/v2/dns_event", json={"result": [{"event_time": "event_time"}]})
        send_events_to_xsiam_mock = mocker.patch("InfobloxBloxOneThreatDefenseEventCollector.send_events_to_xsiam")
        main()
        assert send_events_to_xsiam_mock.call_count == 0
        assert return_results_mock.call_args.args[0].outputs == [{"_time": "event_time", "event_time": "event_time"}]
        assert return_results_mock.call_args.args[0].outputs_prefix == "TestGetEvents"