KnowBe4 KMSAT Event Collector

KnowBe4_KMSAT allows you to push and pull your external data to and from the KnowBe4 console.

Analytics & SIEM · KMSAT

Details

IDKnowBe4 KMSAT Event Collector
ProviderVista Equity Partners
CategoryAnalytics & SIEM
From Version6.8.0
Docker Imagedemisto/python3:3.12.13.10116658
Supported ModulesAgentix XSIAM

README

Allows you to push and pull your external data to and from the KnowBe4 console.

Configure KnowBe4 KMSAT Event Collector in Cortex

Parameter Description Required
Your server URL   True
API Key The API Key to use for connection. For more information about how to generate an API Key, refer to https://support.knowbe4.com/hc/en-us/articles/360024863474-User-Event-API True
First fetch time interval The time range to consider for the initial data fetch. (<number> <unit>, e.g., 2 days, 2 months, 2 years). Default is 1 day. False
Events Fetch Interval The Fetch interval. It is recommended to set it to 5 hours as there are not many events for this API and there’s an api-calls daily-limit for the basic API key. False
Trust any certificate (not secure)   False
Use system proxy settings   False

Important Notes
The basic API-Key has a daily limit of calls per seat.
Therefore, the default and recommended Events Fetch Interval value is 5 hours and
First fetch time interval is 1 day.

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.

kms-get-events

Manual command to fetch events and display them.

Base Command

kms-get-events

Input

Argument Name Description Required
occurred_date Filter by the date the event occurred (YYYY-MM-DD). Optional
risk_level Filter by the risk level by entering a value from -10 (low risk) to 10 (high risk). Optional
per_page The number of results to display per page. The maximum and default is 100. Optional
page The results page to display. Optional
should_push_events Set this argument to True in order to create events, otherwise the command will only display them. If setting to ‘False’, the returned events will be lost. Possible values are: True, False. Default is False. Required

Context Output

Path Type Description
KMSat.Event.id Number Event ID.
KMSat.Event.user.email String The target mail for this event.
KMSat.Event.user.id Number The ID of the user the event is targeted to.
KMSat.Event.user.archived Boolean Whether the user is archived or not.
KMSat.Event.external_id String The event’s external ID.
KMSat.Event.source String The source of the event.
KMSat.Event.description String The event description.
KMSat.Event.occurred_date String The date the event occurred.
KMSat.Event.risk.level Number The event’s risk level.
KMSat.Event.risk.factor Number The event’s risk factor.
KMSat.Event.risk.decay_mode String The risk’s decay mode.
KMSat.Event.risk.expire_date String The event’s expiration date.
KMSat.Event.event_type.id Number The ID of the event type.
KMSat.Event.event_type.name String The name of the event type.

Command example

!kms-get-events should_push_events=false

Context Example

{
    "KMSat": {
        "Event": [
            {
                "account_id": 52306,
                "description": "My description",
                "event_type": {
                    "description": null,
                    "id": 418927900,
                    "name": "my_custom_event"
                },
                "external_id": null,
                "id": "2b265035-1a12-4e76-bcb1-6c681b86333e",
                "metadata": null,
                "occurred_date": "2022-08-04T14:14:50.917Z",
                "risk": {
                    "decay_mode": 0,
                    "expire_date": null,
                    "level": 5
                },
                "source": null,
                "user": {
                    "archived": false,
                    "email": "example@example.com",
                    "id": 38651943
                }
            }
        ]
    }
}

Human Readable Output

KnowBe4 KMSAT Logs

AccountId Description EventType Id OccurredDate Risk User
52306 My description lkjhy khl lgf id: 420899085
name: event_type_55
description: null
786a515c-1cbd-4a8c-a94a-61ad877c893c 2022-08-09T10:05:13.890Z level: 5
decay_mode: 0
expire_date: null
email: maizen@example.com
id: 38651943
archived: false
52306 My description lkjhy khl lgf id: 420894024
name: event_type_2
description: null
c3081dfc-1bf9-4c56-b6ff-f364f0c13d39 2022-08-09T10:01:45.862Z level: 5
decay_mode: 0
expire_date: null
email: maizen@example.com
id: 38651943
archived: false
52306 My description id: 418927900
name: my_custom_event
description: null
2b265035-1a12-4e76-bcb1-6c681b86333e 2022-08-04T14:14:50.917Z level: 5
decay_mode: 0
expire_date: null
email: maizen@example.com
id: 38651943
archived: false

Configuration parameters

  • url — Your server URL (required)
  • credentials — (required)
  • first_fetch — First fetch time interval
  • eventFetchInterval — Events Fetch Interval
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings

Commands (1)

  • kms-get-events

    Manual command to fetch and display events.

import datetime
import json

import pytest
from CommonServerPython import parse_date_string
from KnowBe4KMSATEventCollector import Client


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


MOCK_ENTRY = util_load_json("test_data/mock_event.json")
BASE_URL = "https://api.events.knowbe4.com"


def test_test_module(requests_mock):
    """
    Given:
        - test-module call
    When:
        - A response with an OK status_code is retrieved from the API call.
    Then:
        - Make sure 'ok' is returned.
    """
    from KnowBe4KMSATEventCollector import test_module

    requests_mock.get(f"{BASE_URL}/events", json=MOCK_ENTRY)
    assert test_module(Client(base_url=BASE_URL)) == "ok"


@pytest.mark.parametrize(
    "last_run, mock_item, expected_last_run, expected_fetched_events",
    [
        (
            {"latest_event_time": "2022-08-05T10:05:03.000Z"},
            MOCK_ENTRY,
            {"latest_event_time": "2022-08-09T10:05:13.890Z"},
            MOCK_ENTRY.get("data", [])[0:-1],
        ),
        (
            {"latest_event_time": parse_date_string("2022-08-05T10:05:03.000Z")},
            MOCK_ENTRY,
            {"latest_event_time": "2022-08-09T10:05:13.890Z"},
            MOCK_ENTRY.get("data", [])[0:-1],
        ),
    ],
)
def test_fetch_events(requests_mock, last_run, mock_item, expected_last_run, expected_fetched_events):
    """
    Given:
        - last_run marker and a mock with 2 events that occurred after the last run and 1 that occurred before.
        - case 1: The last run object is a string (an example of last run that was fetched from previous interval).
        - case 2: The last run object is a datetime object (an example of last run that was created by a default datetime.now()
                  in the previous interval due to no fetched events).
    When:
        - Calling fetch events.
    Then:
        - Make sure 2 events returned.
        - Verify the new lastRun is calculated correctly.
        - Verify that the function handle the different last_run types correctly.
    """
    from KnowBe4KMSATEventCollector import fetch_events

    requests_mock.get(f"{BASE_URL}/events", json=mock_item)
    events, new_last_run = fetch_events(Client(base_url=BASE_URL), last_run=last_run)
    assert expected_last_run == new_last_run
    assert events == expected_fetched_events
    assert len(events) == len(expected_fetched_events)


@pytest.mark.parametrize(
    "last_run, fetched_events, expected_filtered_list_size, expected_filtered_list_elements",
    [
        (
            {"latest_event_time": datetime.datetime(2022, 5, 17, 10, 5, 3)},
            [{"occurred_date": "2022-08-09T10:05:13.890Z"}],
            1,
            [{"occurred_date": "2022-08-09T10:05:13.890Z"}],
        ),
        ({"latest_event_time": datetime.datetime(2022, 5, 17, 10, 5, 3)}, [{"occurred_date": "2022-03-09T10:05:13.890Z"}], 0, []),
        (
            {"latest_event_time": datetime.datetime(2022, 5, 17, 10, 5, 3)},
            [{"occurred_date": "2022-08-09T10:05:13.890Z"}, {"occurred_date": "2022-03-09T10:05:13.890Z"}],
            1,
            [{"occurred_date": "2022-08-09T10:05:13.890Z"}],
        ),
    ],
)
def test_eliminate_duplicated_events(last_run, fetched_events, expected_filtered_list_size, expected_filtered_list_elements):
    """
    Given
    - A list of fetched events and a last_run date
    - Case 1: last_run object and a list with 1 event that occurred after the last_run.
    - Case 2: last_run object and a list with 1 event that occurred before the last_run.
    - Case 3: last_run object and a list with 1 event that occurred after the last_run and 1 event that occurred before it.

    When
    - Running eliminate_duplicated_events helper function.

    Then
    - Validate that all the events with the earlier than last_run 'occurred_date' are filtered out.
    - Case 1: Ensure that the event wasn't filtered out of the events list.
    - Case 2: Ensure that the event was filtered out of the events list.
    - Case 3: Ensure that the event that occurred after that last run wasn't filtered out of the events list
              and that the event that occurred before the last run was filtered.
    """
    from KnowBe4KMSATEventCollector import eliminate_duplicated_events

    filtered_events_list = eliminate_duplicated_events(fetched_events, last_run)
    assert len(filtered_events_list) == expected_filtered_list_size
    for event in filtered_events_list:
        assert event in expected_filtered_list_elements


@pytest.mark.parametrize(
    "last_run, events, expected_results",
    [
        ({"latest_event_time": datetime.datetime(2022, 5, 17, 10, 5, 3)}, [{"occurred_date": "2022-08-09T10:05:13.890Z"}], False),
        ({"latest_event_time": datetime.datetime(2022, 5, 17, 10, 5, 3)}, [{"occurred_date": "2022-03-09T10:05:13.890Z"}], True),
    ],
)
def test_check_if_last_run_reached(last_run, events, expected_results):
    """
    Given
    - A list of fetched events and a last_run date.
    - Case 1: latest event that occurred before the first event in the events list.
    - Case 2: latest event that occurred after the first event in the events list.

    When
    - Running check_if_last_run_reached helper function.

    Then
    - Validate that the answer returend from the function is correct.
    - Case 1: Ensure that the function returned False.
    - Case 2: Ensure that the function returend True.
    """
    from KnowBe4KMSATEventCollector import check_if_last_run_reached

    assert check_if_last_run_reached(last_run, events[0]) == expected_results


@pytest.mark.parametrize(
    "mock_item, expected_results, expected_length",
    [(MOCK_ENTRY, MOCK_ENTRY.get("data", []), 3), ({}, "No events were found.", 21)],
)
def test_get_events(requests_mock, mock_item, expected_results, expected_length):
    """
    Given:
        - a mock response.
        - Case 1: A mock response with 3 events.
        - Case 2: Empty mock response.
    When:
        - Running the kms-get-events command.
    Then:
        - Make sure all of the events are returned as part of the CommandResult.
        - Case 1: Ensure the same 3 events were found.
        - Case 2: Should print that no events we found.
    """
    from KnowBe4KMSATEventCollector import get_events_command

    requests_mock.get(f"{BASE_URL}/events", json=mock_item)
    args = {"should_push_events": False}

    results = get_events_command(Client(base_url=BASE_URL), args=args, vendor="", product="")

    if mock_item:
        assert results.outputs == expected_results
        assert len(results.outputs) == expected_length
    else:
        assert results.readable_output == expected_results
        assert len(results.readable_output) == expected_length