IBMSecurityVerify

IBM Security Verify provides a secure and scalable solution for collecting and managing security events from IBM Security Verify, offering advanced threat detection and response capabilities for protecting identities, applications, and data.

Analytics & SIEM · IBM Security Verify

Details

IDIBMSecurityVerify
ProviderIBM
CategoryAnalytics & SIEM
From Version8.4.0
Docker Imagedemisto/python3:3.12.13.10116658
Supported ModulesXSIAM

README

IBM Security Verify provides a secure and scalable solution for collecting and managing security events from IBM Security Verify, offering advanced threat detection and response capabilities for protecting identities, applications, and data.

Set up the Third Party System

To obtain the Client ID and Client Secret, follow these steps:

  1. Log in to the IBM Security Verify UI.
  2. Click the profile icon located at the top right corner of the interface.
  3. Select Switch to admin to access administrative settings.
  4. Navigate to Security > API Access.
  5. Click Add API Client to generate the necessary credentials.
  6. After clicking Add API Client, make sure to assign the following permissions to the API client:
    • Manage reports
    • Read reports
  • Creating an API Client

Configure IBM Security Verify on Cortex XSIAM

  1. Navigate to Settings > Configurations > Data Collection > Automations & Feed Integrations.
  2. Search for IBM Security Verify.
  3. Click Add instance to create and configure a new integration instance.

    Parameter Description Required
    Server URL For example: https://tenant.verify.ibm.com True
    Client ID   True
    Client Secret   True
    The maximum number of events per fetch The maximum is 50,000. True
    Trust any certificate (not secure)   False
    Use system proxy settings   False
  4. Click Test to validate the URLs, token, and connection.

Commands

You can execute these commands from the Cortex XSIAM 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.

ibm-security-verify-get-events


Retrieves events from IBM Security Verify.

Base Command

ibm-security-verify-get-events

Input

Argument Name Description Required
should_push_events If set to ‘True’, the command will create events; otherwise, it will only display them. Possible values are: True, False. Default is False. Optional
limit Maximum number of results to return. Default is 1000. Optional
last_id The ID of the last event retrieved. Use together with last_time for pagination to get events after this ID. Example: 1234abcd-5678-90ef-1234-567890abcdef. Optional
last_time The timestamp of the last event retrieved. Use together with last_id for pagination to get events after this time. Example: 1672531200000. Optional
sort_order Order to sort events by: ‘Desc’ or ‘Asc’. Possible values are: Desc, Asc. Default is Desc. Optional

Context Output

There is no context output for this command.

Configuration parameters

  • url — Server URL (required)
  • credentials — Client ID (required)
  • max_fetch — The maximum number of events per fetch (required)
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings

Commands (1)

  • ibm-security-verify-get-events

    Retrieves events from IBM Security Verify.

from datetime import UTC, datetime, timedelta

import pytest
from CommonServerPython import *
from freezegun import freeze_time
from IBMSecurityVerify import Client, fetch_events, get_events_command, max_limit_validation

EVENTS = [{"indexed_at": "2", "tenantname": "Test Event 2", "id": "123"}]

RESPONSE = {"response": {"events": {"events": EVENTS}}}


@pytest.fixture()
def mock_client(mocker) -> Client:
    mocker.patch.object(Client, "_authenticate")
    client = Client(
        base_url="https://www.example.com",
        client_id="DUMMY_CLIENT_ID",
        client_secret="DUMMY_SECRET_KEY",
        verify=False,
        proxy=False,
    )
    client._headers = {"Authorization": "Bearer DUMMY_TOKEN"}
    return client


@pytest.mark.parametrize(
    "token_data, expected_result",
    [
        ({"access_token": "valid_token", "expiry_time_utc": (datetime.now(UTC) + timedelta(minutes=5)).isoformat()}, True),
        ({"access_token": "valid_token", "expiry_time_utc": datetime.now(UTC).isoformat()}, False),
    ],
)
def test_is_token_valid(mock_client, token_data, expected_result):
    result = mock_client._is_token_valid(token_data)
    assert result == expected_result


@freeze_time("2024-08-29 12:00:00")
def test_get_new_token(mocker, mock_client):
    expires_in = 7200
    expiry_time_utc = datetime.now(UTC) + timedelta(seconds=expires_in)
    expected_result = {"access_token": "DUMMY_TOKEN", "expiry_time_utc": expiry_time_utc.isoformat()}

    response = {"access_token": "DUMMY_TOKEN", "expires_in": expires_in}
    http_request = mocker.patch.object(Client, "_http_request", return_value=response)

    rustle = mock_client._get_new_token()

    http_request.assert_called_with(
        method="POST",
        url_suffix="/endpoint/default/token",
        data={
            "client_id": "DUMMY_CLIENT_ID",
            "client_secret": "DUMMY_SECRET_KEY",
            "grant_type": "client_credentials",
        },
    )

    assert rustle == expected_result


def test_max_limit_validation(mock_client):
    MAX_LIMIT = 50_000
    max_limit_validation(1_000)
    with pytest.raises(DemistoException):
        max_limit_validation(MAX_LIMIT + 1)
    with pytest.raises(DemistoException):
        max_limit_validation(0)


def test_search_events(mocker, mock_client):
    http_request = mocker.patch.object(Client, "_http_request", return_value=RESPONSE)

    limit = 2
    sort_order = "asc"
    last_item = {"last_id": "123", "after_time": "456"}

    _, events = mock_client.search_events(limit, sort_order, last_item)

    expected_events = [{"indexed_at": "2", "tenantname": "Test Event 2", "id": "123"}]
    assert events == expected_events

    http_request.assert_called_with(
        method="GET",
        url_suffix="events",
        params={
            "size": limit,
            "range_type": "indexed_at",
            "all_events": "yes",
            "sort_order": sort_order,
            "after_time": last_item.get("last_time"),
            "after_id": last_item.get("last_id"),
        },
    )


def test_get_events_command(mocker, mock_client):
    """ """
    args = {"limit": 2, "sort_order": "Desc", "last_id": "123", "last_time": "456"}

    search_events = mocker.patch.object(mock_client, "search_events", return_value=({}, []))
    get_events_command(mock_client, args)

    search_events.assert_called_with(limit=2, sort_order="desc", last_item={"last_id": "123", "last_time": "456"})


def test_fetch_events(mocker, mock_client):
    """ """
    # First fetch
    search_events = mocker.patch.object(mock_client, "search_events", return_value=({}, EVENTS))
    last_run = {}

    # Verify the first fetch initializes last_run with the latest event
    last_run, _ = fetch_events(client=mock_client, last_run=last_run, limit=2)
    search_events.assert_any_call(limit=1, sort_order="desc")

    # Second fetch
    updated_last_run, _ = fetch_events(client=mock_client, last_run=last_run, limit=2)

    # Verify that the second fetch uses the last_run from the first fetch
    search_events.assert_any_call(limit=2, sort_order="asc", last_item=updated_last_run)