RunZero Event Collector

This is the RunZero event collector integration for XSIAM.

Analytics & SIEM · RunZero

Details

IDRunZero Event Collector
ProviderrunZero
CategoryAnalytics & SIEM
From Version6.8.0
Docker Imagedemisto/python3:3.12.13.10116658
Supported ModulesXSIAM

README

This is the RunZero event collector integration for XSIAM.
This integration was integrated and tested with version 3.3.0 of RunZero Event Collector

Configure RunZero Event Collector in Cortex

Parameter Description Required
Server URL (e.g., https://console.runzero.com/)   True
Fetch incidents   False
Maximum number of incidents per fetch   False
Client secret The client secret to access the service REST API. True
Client id The client ID as defined in RunZero. True
First fetch time   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.

runzero-get-events


Gets events from RunZero.

Base Command

runzero-get-events

Input

Argument Name Description Required
should_push_events If true, the command will create events, otherwise it will only display them. Possible values are: true, false. Default is false. Required
limit Maximum results to return. Optional
start_time Filter by start time.
Examples:
“3 days ago”
“1 month”
“2019-10-10T12:22:00”
“2019-10-10”.
Optional

Context Output

There is no context output for this command.

Configuration parameters

  • url — Server URL (required)
  • client_id — Client ID (required)
  • client_secret — (required)
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings
  • isFetch — Fetch incidents
  • max_fetch — Max events per fetch
  • first_fetch — First fetch timestamp (<number> <time unit>, e.g., 12 hours, 7 days)

Commands (1)

  • runzero-get-events

    Gets events from RunZero.

import json

import demistomock as demisto  # noqa: F401
import pytest
from CommonServerPython import DemistoException

BASE_URL = "https://console.runzero.com/api/v1.0"


def util_load_json(path):
    with open(path, encoding="utf-8") as f:
        return json.loads(f.read())


def get_actual_events():
    return [
        {
            "id": "991926c7-2d35-47fb-a146-e587db439c8e",
            "created_at": 1673732462,
            "client_id": "client_uuid",
            "organization_id": "organization_uuid",
            "site_id": "00000000-0000-0000-0000-000000000000",
            "action": "agent-offline",
            "source_id": "source_uuid",
            "source_name": "M-source_name_identifier",
            "source_type": "agent",
            "target_id": "organization_uuid",
            "target_name": "target_name",
            "target_type": "organization",
            "success": True,
            "details": {
                "agent_external_ip": "agent_external_ip",
                "agent_host_id": "7baf78c1a76f179ec1ef93b6c6e5c986",
                "agent_id": "source_uuid",
                "agent_internal_ip": "agent_internal_ip",
                "agent_last_seen": 1673732462,
                "agent_name": "M-source_name_identifier",
                "agent_os": "darwin",
                "agent_tags": "",
                "agent_version": "3.4.16 (build 20230111225731) [d3db1a68496adeccad22e606a2ee947a7e9acc04]",
                "organization_id": "organization_uuid",
                "organization_name": "target_name",
                "site_id": "00000000-0000-0000-0000-000000000000",
                "site_name": "",
            },
            "state": "processed",
            "processor_id": "00000000-0000-0000-0000-000000000000",
            "processed_at": 1673732475,
            "_time": "2023-01-14T21:41:02.000Z",
        },
        {
            "id": "a0d93736-fe84-4948-be43-d862011ab7e2",
            "created_at": 1673738408,
            "client_id": "client_uuid",
            "organization_id": "organization_uuid",
            "site_id": "00000000-0000-0000-0000-000000000000",
            "action": "agent-status",
            "source_id": "source_uuid",
            "source_name": "M-source_name_identifier",
            "source_type": "agent",
            "target_id": "organization_uuid",
            "target_name": "target_name",
            "target_type": "organization",
            "success": True,
            "details": {
                "agent_connected": False,
                "agent_external_ip": "agent_external_ip",
                "agent_host_id": "7baf78c1a76f179ec1ef93b6c6e5c986",
                "agent_id": "source_uuid",
                "agent_internal_ip": "agent_internal_ip",
                "agent_last_seen": 1673732462,
                "agent_name": "M-source_name_identifier",
                "agent_os": "darwin",
                "agent_tags": "",
                "agent_version": "3.4.16 (build 20230111225731) [d3db1a68496adeccad22e606a2ee947a7e9acc04]",
                "organization_id": "organization_uuid",
                "organization_name": "target_name",
                "site_id": "00000000-0000-0000-0000-000000000000",
                "site_name": "",
            },
            "state": "",
            "processor_id": "00000000-0000-0000-0000-000000000000",
            "processed_at": 0,
            "_time": "2023-01-14T23:20:08.000Z",
        },
    ]


def get_client():
    from RunZeroEventCollector import Client

    return Client(
        base_url=BASE_URL,
        verify=False,
        proxy=False,
        client_id="",
        client_secret="",
    )


def test_sort_events_by_ids():
    from RunZeroEventCollector import sort_events

    mock_response = util_load_json("test_data/system_event_logs.json")
    events_sorted = sort_events(mock_response)
    for i in range(1, len(events_sorted)):
        assert events_sorted[i]["created_at"] > events_sorted[i - 1]["created_at"]


def test_get_events_command(requests_mock):
    """
    Tests the get-events command function.

        Given:
            - requests_mock instance to generate the appropriate get_alert API response,
              loaded from a local JSON file.

        When:
            - Running the 'get_events_command' command.

        Then:
            - Checks the output of the command function with the expected output.
    """
    from RunZeroEventCollector import get_events_command

    mock_response = util_load_json("test_data/system_event_logs.json")
    requests_mock.post("https://console.runzero.com/api/v1.0/account/api/token", json={"access_token": "access_token"})

    requests_mock.get(
        "https://console.runzero.com/api/v1.0/account/events.json?search=created_at:>1673719953", json=mock_response
    )

    client = get_client()

    events, commandResult = get_events_command(client=client, query_string="created_at:>1673719953", limit=2)

    assert events == get_actual_events()


def test_fetch_events(requests_mock):
    """
    Tests the fetch-incidents command function.

        Given:
            - requests_mock instance to generate the appropriate get_alert API response,
              loaded from a local JSON file.

        When:
            - Running the 'fetch_incidents' command.

        Then:
            - Checks the output of the command function with the expected output.
    """
    from RunZeroEventCollector import fetch_events

    mock_response = util_load_json("test_data/system_event_logs.json")

    requests_mock.post("https://console.runzero.com/api/v1.0/account/api/token", json={"access_token": "access_token"})

    requests_mock.get(
        "https://console.runzero.com/api/v1.0/account/events.json?search=created_at:>1673719953", json=mock_response
    )

    client = get_client()

    last_run = {"last_fetch": 1673719953}

    _, events = fetch_events(
        client=client,
        max_results=2,
        last_run=last_run,
        first_fetch_time=1673719953,
    )

    assert events == get_actual_events()


def test_parse_event():
    from RunZeroEventCollector import add_time_to_event

    my_json = util_load_json("test_data/system_event_logs.json")
    parsed_event = add_time_to_event(my_json[0])
    assert parsed_event == get_actual_events()[0]


def test_fetch_system_event_logs(requests_mock):
    client = get_client()
    expected_res = util_load_json("test_data/system_event_logs.json")[0]
    requests_mock.post("https://console.runzero.com/api/v1.0/account/api/token", json={"access_token": "access_token"})
    requests_mock.get("https://console.runzero.com/api/v1.0/account/events.json", json=expected_res)

    actual_response = client.fetch_system_event_logs("search_query")
    assert actual_response == expected_res


def test_get_api_token(requests_mock):
    client = get_client()
    requests_mock.post("https://console.runzero.com/api/v1.0/account/api/token", exc=Exception("Forbidden"))

    with pytest.raises(DemistoException) as e:
        client.get_api_token()
    assert e.value.message == "Authorization Error: make sure API Key is correctly set"


def test_http_request(requests_mock):
    client = get_client()
    expected_res = util_load_json("test_data/system_event_logs.json")[0]
    requests_mock.post("https://console.runzero.com/api/v1.0/account/api/token", json={"api_token": "api_token"})
    requests_mock.get("https://console.runzero.com/api/v1.0/account/events.json", json=expected_res)
    actual_response = client.http_request("GET", "/account/events.json", {})
    assert actual_response == expected_res


def test_test_module(requests_mock):
    from RunZeroEventCollector import test_module

    client = get_client()
    expected_res = util_load_json("test_data/system_event_logs.json")

    requests_mock.post("https://console.runzero.com/api/v1.0/account/api/token", json={"access_token": "access_token"})

    requests_mock.get("https://console.runzero.com/api/v1.0/account/events.json", json=expected_res)

    raw = test_module(client, 1)
    assert raw == "ok"


def test_main(mocker):
    """
    Given:
        - All return values from helper functions are valid
    When:
        - main function test-module is executed
    Then:
        - Return ok result to War-Room
    """
    from RunZeroEventCollector import main

    mocker.patch.object(
        demisto,
        "params",
        return_value={
            "client_id": "",
            "url": "",
            "client_secret": {"password": "test_api"},
        },
    )
    mocker.patch("RunZeroEventCollector.Client.get_api_token", return_value={"access_token": "access_token"})
    mocker.patch("RunZeroEventCollector.Client.http_request", return_value=util_load_json("test_data/system_event_logs.json"))
    mocker.patch.object(demisto, "command", return_value="test-module")
    mocker.patch.object(demisto, "results")
    main()
    assert demisto.results.call_count == 1
    assert demisto.results.call_args[0][0] == "ok"