ExtrahopRevealXEventCollector

ExtraHop Reveal(x) is a network detection and response solution that provides complete visibility of network communications at enterprise scale, real-time threat detections backed by machine learning, and guided investigation workflows that simplify response.

Analytics & SIEM · ExtraHop Reveal(x)

Details

IDExtrahopRevealXEventCollector
ProviderBain Capital Private Equity
CategoryAnalytics & SIEM
From Version6.10.0
Docker Imagedemisto/python3:3.12.13.10116658
Supported ModulesAgentix XSIAM EDR Cortex Cloud Cloud Runtime Security

README

ExtraHop Reveal(x) is a network detection and response solution that provides complete visibility of network communications at enterprise scale, real-time threat detections backed by machine learning, and guided investigation workflows that simplify response.

This integration works with ExtraHop firmware version greater than or equal to 9.3.0

Configure an instance for ExtraHop Reveal(x)

How to create REST API Credentials

  • You must have system and access administration privileges.
  1. Log in to RevealX 360.
  2. Click the System Settings icon - at the top right of the page and then click All Administration.
  3. Click API Access.
  4. Click Create Credentials.
  5. In the Name field, type a name for the credentials.
  6. In the Privileges field, specify a privilege level for the credentials. The privilege level determines which actions can be performed with the credential. Do not grant more privileges to REST API credentials than needed because it can create a security risk. For example, applications that only retrieve metrics should not be granted credentials that grant administrative privileges. For more information about each privilege level, see User privileges.
  • Note: System and Access Administration privileges are similar to Full write privileges and allow the credentials to connect sensors and Trace appliances to RevealX 360.*
  1. In the Packet Access field, specify whether you can retrieve packets and session keys with the credentials.
  2. Click Save. The Copy REST API Credentials pane appears.
  3. Under ID, click Copy to Clipboard and save the ID to your local machine.
  4. Under Secret, click Copy to Clipboard and save the secret to your local machine.
  5. Click Done.

Configure ExtraHop Reveal(x) in Cortex

Parameter Description Required
Your server URL   True
Client Id The client ID generated on your ExtraHop system that is required for authentication if connecting to ExtraHop Reveal(x) 360. True
Client Secret The client secret generated on your ExtraHop system that is required for authentication if connecting to ExtraHop Reveal(x) 360. True
Trust any certificate (not secure)   False
Use system proxy settings   False
Fetch events   False
Maximum number of events per fetch Defines the maximum number of audits events per fetch cycle. Default value: 25000. True

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.

revealx-get-events


Retrieves a list of audit logs events from the Extrahop RevealX instance.

Base Command

revealx-get-events

Input

Argument Name Description Required
should_push_events Set this argument to true in order to create events, otherwise it will only display them. Possible values are: true, false. Default is false. Required
max_events Returns no more than the specified number of detections. Optional

Context Output

There is no context output for this command.

Configuration parameters

  • server_url — Server URL (required)
  • credentials — Client ID (required)
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings
  • isFetchEvents — Fetch events
  • max_events_per_fetch — Maximum number of events per fetch

Commands (1)

  • revealx-get-events

    Retrieves a list of audit logs events from the Extrahop RevealX instance.

import pytest

from ExtrahopRevealXEventCollector import Client
from CommonServerPython import *

MOCK_BASEURL = "https://example.com"
MOCK_CLIENT_ID = "ID"
MOCK_CLIENT_SECRET = "SECRET"
OK_CODES = (200, 201, 204)


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


@pytest.fixture
def client():
    return Client(
        base_url=MOCK_BASEURL,
        verify=False,
        client_id=MOCK_CLIENT_ID,
        client_secret=MOCK_CLIENT_SECRET,
        use_proxy=False,
        ok_codes=OK_CODES,
    )


def test_update_time_values_detections():
    """
    Given: A mock raw response containing detections logs.
    When: Updating time fields
    Then: Ensure the events are added the new time fields
    """
    from ExtrahopRevealXEventCollector import update_time_values_detections

    raw_detections = util_load_json("test_data/detections-dummy.json")
    update_time_values_detections(raw_detections)

    for detection in raw_detections:
        assert "_TIME" in detection
        assert "_ENTRY_STATUS" in detection


def test_fetch_events_update_last_run(client, mocker):
    """
    Given: A mock raw response containing detections logs.
    When: fetching events.
    Then: Make sure that the last run object was updated as expected
    """
    from ExtrahopRevealXEventCollector import fetch_events

    raw_detections = util_load_json("test_data/detections-dummy.json")
    mocker.patch("ExtrahopRevealXEventCollector.Client.detections_list", return_value=raw_detections)

    output, new_last_run = fetch_events(client, last_run={}, max_events=len(raw_detections))

    assert len(output) == 5
    assert new_last_run.get("offset") == 0
    assert new_last_run.get("detection_start_time") == raw_detections[-1]["mod_time"] + 1


def test_fetch_events_already_fetched(client, mocker):
    """
    Given: A mock raw response containing detections events.
    When: Fetching events that was already fetched
    Then: Ensure the function does not return any events
    """
    from ExtrahopRevealXEventCollector import fetch_events

    raw_detections = util_load_json("test_data/detections-dummy.json")
    mocker.patch("ExtrahopRevealXEventCollector.Client.detections_list", return_value=raw_detections)

    mock_already_fetched = [d["id"] for d in raw_detections]
    last_run_mock = {"already_fetched": mock_already_fetched}

    output, new_last_run = fetch_events(client, last_run=last_run_mock, max_events=len(raw_detections))

    assert len(output) == 0
    assert new_last_run.get("already_fetched") == mock_already_fetched


def test_fetch_events_reaching_limit(client, mocker):
    """
    Given: A mock raw response containing detections events.
    When: Fetching events with a fetch limit higher than the number of available logs.
    Then: Ensure the function returns exactly the requested number of events and updates the last run timestamp correctly.
    """
    from ExtrahopRevealXEventCollector import fetch_events

    raw_detections = util_load_json("test_data/detections-dummy.json")[:-2]
    mocker.patch("ExtrahopRevealXEventCollector.Client.detections_list", return_value=raw_detections)

    output, new_last_run = fetch_events(client, last_run={}, max_events=len(raw_detections) + 2)

    assert len(output) == len(raw_detections)
    assert new_last_run.get("detection_start_time") == raw_detections[-1]["mod_time"] + 1


def test_fetch_events_more_than_exist(client, mocker):
    """
    Given: A mock raw response containing detections events.
    When: Fetching events with a fetch limit smaller than the number of available logs.
    Then: Ensure the function returns exactly the requested number of events and updates the last run timestamp correctly.
    """
    from ExtrahopRevealXEventCollector import fetch_events

    raw_detections = util_load_json("test_data/detections-dummy.json")
    mocker.patch("ExtrahopRevealXEventCollector.Client.detections_list", return_value=raw_detections)

    output, new_last_run = fetch_events(client, last_run={}, max_events=len(raw_detections) - 2)

    assert len(output) == len(raw_detections) - 2
    assert new_last_run.get("detection_start_time") == raw_detections[-3]["mod_time"] + 1


def test_fetch_events_same_mod_time(client, mocker):
    """
    Given: A mock raw response containing detections events.
    When: Fetching events with a fetch limit less than the number of available logs and they all have the same mod_time
    Then: Ensure the function returns exactly the requested number of events and updates the last run timestamp correctly.
    """
    from ExtrahopRevealXEventCollector import fetch_events

    raw_detections = util_load_json("test_data/detections-dummy.json")
    mod_time_all = 1000
    for d in raw_detections:
        d["mod_time"] = mod_time_all

    mocker.patch("ExtrahopRevealXEventCollector.Client.detections_list", return_value=raw_detections)

    output, new_last_run = fetch_events(client, last_run={}, max_events=len(raw_detections) - 2)

    assert len(output) == len(raw_detections) - 2
    assert new_last_run.get("detection_start_time") == mod_time_all


def test_authenticate_uses_basic_auth(requests_mock) -> None:
    """Test that authenticate sends client credentials via HTTP Basic Auth, not in the request body.

    Given:
        - An ExtraHop Event Collector client with client_id and client_secret.
    When:
        - authenticate is called to obtain an access token.
    Then:
        - The POST to /oauth2/token uses HTTP Basic Auth with client_id and client_secret.
        - The request body does NOT contain client_id or client_secret.
        - The returned token and expiry match the mocked response.
    """
    mock_response = {"access_token": "fake-token", "expires_in": 3600, "token_type": "Bearer"}
    token_request = requests_mock.post(f"{MOCK_BASEURL}/oauth2/token", json=mock_response)

    client = Client(
        base_url=MOCK_BASEURL,
        verify=False,
        client_id=MOCK_CLIENT_ID,
        client_secret=MOCK_CLIENT_SECRET,
        use_proxy=False,
        ok_codes=OK_CODES,
    )

    token, expires_in = client.authenticate(MOCK_CLIENT_ID, MOCK_CLIENT_SECRET)

    assert token == mock_response["access_token"]
    assert expires_in == mock_response["expires_in"]

    last_request = token_request.last_request
    assert last_request.headers.get("Authorization", "").startswith("Basic ")
    body = last_request.text
    assert "client_id" not in body
    assert "client_secret" not in body