MongoDBAtlasEventCollector
MongoDB Atlas is an integration that supports fetching and managing alerts and events within Cortex XSIAM.
Analytics & SIEM · MongoDB Atlas
Details
| ID | MongoDBAtlasEventCollector |
|---|---|
| Provider | MongoDB Inc. |
| Category | Analytics & SIEM |
| From Version | 6.10.0 |
| Docker Image | demisto/python3:3.12.13.10116658 |
| Supported Modules | XSIAM |
README
MongoDB Atlas is an integration that supports fetching and managing alerts and events within Cortex XSIAM.
This integration was integrated and tested with version 2.0 of MongoDB Atlas.
Configure MongoDB Atlas on Cortex XSIAM
- Navigate to Settings > Configurations > Automation & Feed Integrations.
- Search for MongoDB Atlas.
-
Click Add instance to create and configure a new integration instance.
Parameter Description Required Server URL The endpoint URL. True Public Key The Public Key to use for connection. True Private Key The Private Key to use for connection. True Group ID The Project ID from MongoDB Atlas account. True Maximum number of events per fetch Defines the maximum number of alerts or events fetched per type in each fetch cycle. Default value: 2500. True Trust any certificate (not secure) False Use system proxy settings False - Click Test to validate the URLs, keys, and connection.
To create an API key for a project using the MongoDB Atlas UI
- Log in to MongoDB Atlas.
- Click Access Manager in the navigation bar, then click your project.
- Navigate to Applications.
- Click Create Application and then click API Key.
- Enter a Description and set Project Permissions. For reading alerts and events, you can set the Project Permissions to “Read Only”.
- Copy and save the Public Key. The public key acts as the username when making API requests.
-
Copy and save the Private Key. The private key acts as the password when making API requests.
WARNING: Save the Private Key securely! The Private Key is only displayed once on this page. Click Copy to copy it to your clipboard. Save and secure both the Public and Private Keys.
- Add an API Access List Entry by clicking Add Access List Entry.
- Enter an IP address from which MongoDB Atlas should accept API requests for this API Key. You can also click Use Current IP Address if the host you are using to access MongoDB Atlas will also make API requests using this API Key.
- Click Save.
- Click Done.
IMPORTANT
You need to allow access from Cortex XSIAM to MongoDB via the UI by adding a Cortex XSIAM IP address:
https://cloud.mongodb.com/v2/#/security/network/accessList
Additional Information
Groups and projects are synonymous terms. Your group ID is the same as your project ID. For existing groups, your group/project ID remains the same. The resource and corresponding endpoints use the term groups.
Authentication and authorization
Grant Programmatic Access to a Project
Use the following procedures to grant programmatic access to a project.
To learn more, see Manage Programmatic Access to a Project.
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.
mongo-db-atlas-get-events
Retrieves a list of events from the MongoDB Atlas instance.
Base Command
mongo-db-atlas-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 |
| limit | Maximum number of events to return. Value range: 1-2500. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| MongoDBAtlasEventCollector | List | The list of the events and the alerts. |
Configuration parameters
url— Server URL (required)credentials— Public Key (required)group_id— Group ID (required)max_events_per_fetch— Maximum number of events per fetch (required)insecure— Trust any certificate (not secure)proxy— Use system proxy settings
Commands (1)
-
mongo-db-atlas-get-eventsRetrieves a list of events from the MongoDB Atlas instance.
import copy import json import pytest DATE_FORMAT = "%Y-%m-%dT%H:%M:%S" MOCK_BASEURL = "https://example.com" MOCK_GROUP_ID = "123" MOCK_PRIVATE_KEY = "private_key" MOCK_PUBLIC_KEY = "public_key" def create_client(): from MongoDBAtlasEventCollector import Client return Client( base_url=MOCK_BASEURL, verify=False, group_id=MOCK_GROUP_ID, private_key=MOCK_PRIVATE_KEY, public_key=MOCK_PUBLIC_KEY ) def util_load_json(path): with open(path, encoding="utf-8") as f: return json.loads(f.read()) def test_add_entry_status_field(): """ Given: A list of events with 'created' and 'updated' timestamps. When: Calling the `add_entry_status_field` function to add the '_ENTRY_STATUS' field based on whether the event has been updated. Then: Ensure the '_ENTRY_STATUS' field is correctly added with the value 'new' if the 'created' and 'updated' timestamps are the same, or 'updated' if the 'updated' timestamp differs from the 'created' timestamp. """ from MongoDBAtlasEventCollector import add_entry_status_field test_cases = [ {"event": {"created": "2024-10-27T12:07:17Z", "updated": "2024-10-27T12:07:17Z"}, "expected_status": "new"}, {"event": {"created": "2024-10-27T12:07:17Z", "updated": "2024-10-27T12:08:17Z"}, "expected_status": "updated"}, ] for case in test_cases: event = case["event"] add_entry_status_field(event) assert event["_ENTRY_STATUS"] == case["expected_status"] def test_get_page_url(): """ Given: A list of links. When: Calling the `get_page_url` function to extract the 'next' URL. Then: Ensure the correct page URL is returned if present, or empty string if no page type URL is found. """ from MongoDBAtlasEventCollector import get_page_url links_with_next = [{"rel": "prev", "href": "page/1"}, {"rel": "next", "href": "page/3"}, {"rel": "last", "href": "page/4"}] assert get_page_url(links_with_next, "next") == "page/3" links_without_next = [ {"rel": "prev", "href": "page/1"}, {"rel": "first", "href": "page/1"}, {"rel": "last", "href": "page/4"}, ] assert get_page_url(links_without_next, "next") == "" links_with_next = [{"rel": "prev", "href": "page/1"}, {"rel": "self", "href": "page/3"}, {"rel": "last", "href": "page/4"}] assert get_page_url(links_with_next, "self") == "page/3" def test_add_time_field(): """ Given: An event with either 'updated' or 'created' timestamp fields. When: Calling the `add_time_field` function to add a '_time' field based on the available timestamp. Then: Ensure the '_time' field is correctly added, using the 'updated' timestamp if present, otherwise using the 'created' timestamp. """ from MongoDBAtlasEventCollector import add_time_field event = {"created": "2024-10-27T12:07:17Z", "updated": "2024-10-27T13:07:17Z"} add_time_field(event) assert event["_time"] == "2024-10-27T13:07:17Z" event = {"created": "2024-10-27T13:07:17Z"} add_time_field(event) assert event["_time"] == "2024-10-27T13:07:17Z" def test_remove_alerts_by_ids(): """ Given: A list of alerts, each with an 'id', and a list of alert IDs to remove. When: Calling remove_alerts_by_ids with the list of alerts and specified IDs. Then: Verify that: - The function returns a list of alerts with only the alerts whose IDs are not in the removal list. - The output matches the expected list, confirming that only the specified alerts were removed. """ from MongoDBAtlasEventCollector import remove_alerts_by_ids alerts = [ {"id": 1, "name": "alert1"}, {"id": 2, "name": "alert2"}, {"id": 3, "name": "alert3"}, {"id": 4, "name": "alert4"}, ] ids_to_remove = [2, 4] expected_result = [{"id": 1, "name": "alert1"}, {"id": 3, "name": "alert3"}] result = remove_alerts_by_ids(alerts, ids_to_remove) assert result == expected_result @pytest.mark.parametrize( "fetch_limit, expected_alert_count", [ (10, 5), # Case: fetch_limit > available alerts (3, 3), # Case: fetch_limit < available alerts ], ) def test_fetch_alert_type(mocker, fetch_limit, expected_alert_count): """ Given: A mock MongoDB Atlas client with a page of alerts and a specified fetch limit. When: Fetching alerts from the page with different fetch limits. Then: Ensure the correct number of alerts are fetched, the page link is set correctly, and the last page alert IDs are correctly recorded and validated. """ from MongoDBAtlasEventCollector import fetch_alerts_command, get_page_url mocked_alerts = util_load_json("test_data/raw_alerts_page_1.json") mocker.patch("MongoDBAtlasEventCollector.Client.get_alerts_request", return_value=mocked_alerts) mocker.patch("MongoDBAtlasEventCollector.get_page_url", side_effect=["", get_page_url]) client = create_client() last_run = {"page_link": None, "last_page_alerts_ids": []} output, last_run_new_dict = fetch_alerts_command(client, fetch_limit, last_run) assert len(output) == expected_alert_count last_page_alerts_ids = last_run_new_dict.get("last_page_alerts_ids") assert len(last_page_alerts_ids) == expected_alert_count for id in last_page_alerts_ids: assert 1 <= int(id) <= expected_alert_count last_page_alerts_ids.remove(id) @pytest.mark.parametrize( "fetch_limit, expected_alert_count", [ (9, 9), # Each page has 5 alerts (8, 8), ], ) def test_fetch_alert_type_using_next_page(mocker, fetch_limit, expected_alert_count): """ Given: A mock MongoDB Atlas client with two pages of alert data. When: Fetching alerts with a specified fetch limit and processing alerts from the first and second pages. Then: Ensure the correct number of alerts are fetched, the next page link is set properly, and the last page alert IDs are correctly updated after the fetch. """ from MongoDBAtlasEventCollector import fetch_alerts_command mocked_alerts_page_1 = util_load_json("test_data/raw_alerts_page_1.json") mocked_alerts_page_2 = util_load_json("test_data/raw_alerts_page_2.json") mocker.patch("MongoDBAtlasEventCollector.Client.get_alerts_request", return_value=mocked_alerts_page_1) mocker.patch("MongoDBAtlasEventCollector.get_page_url", return_value=True) mocker.patch("MongoDBAtlasEventCollector.Client.get_response_from_page_link", return_value=mocked_alerts_page_2) client = create_client() last_run = {"page_link": None, "last_page_alerts_ids": []} output, last_run_new_dict = fetch_alerts_command(client, fetch_limit, last_run) expected_ids_page_1 = [str(i) for i in range(1, expected_alert_count + 1)] assert len(output) == expected_alert_count last_page_alerts_ids = last_run_new_dict.get("last_page_alerts_ids") assert set(last_page_alerts_ids) == set(expected_ids_page_1[5:]) last_run = {"page_link": None, "last_page_alerts_ids": ["1"]} output, last_run_new_dict = fetch_alerts_command(client, fetch_limit, last_run) assert len(output) == expected_alert_count last_page_alerts_ids = last_run_new_dict.get("last_page_alerts_ids") assert len(last_page_alerts_ids) == abs(4 - expected_alert_count) def test_fetch_alert_type_while_more_alerts_created(mocker): """ Given: A mock MongoDB Atlas client with an initial page of alert data, where more alerts are added after the initial fetch. When: Running fetch_alert_type to fetch alerts in two stages - first fetching the initial set, and then fetching only the newly added alerts. Then: Ensure the correct number of alerts are returned in each fetch, that the last page link is set correctly, and that the IDs in last_page_alerts_ids match the expected values after both fetches. """ from MongoDBAtlasEventCollector import fetch_alerts_command mocked_alerts_page_1 = util_load_json("test_data/raw_alerts_page_1.json") mocker.patch("MongoDBAtlasEventCollector.Client.get_alerts_request", return_value=mocked_alerts_page_1) mocker.patch("MongoDBAtlasEventCollector.get_page_url", return_value=False) client = create_client() last_run = {"page_link": None, "last_page_alerts_ids": []} output, last_run_new_dict = fetch_alerts_command(client, len(mocked_alerts_page_1.get("results")), last_run) seen_ids = {event.get("id") for event in output} assert len(output) == len(mocked_alerts_page_1.get("results")) # assert last_run_new_dict.get('page_link') == 'self1' mocked_alerts_page_1_with_more_alerts = util_load_json("test_data/raw_alerts_page_1_with_more_alerts.json") mocker.patch("MongoDBAtlasEventCollector.Client.get_alerts_request", return_value=mocked_alerts_page_1_with_more_alerts) last_run = copy.deepcopy(last_run_new_dict) additional_alerts_amount = len(mocked_alerts_page_1_with_more_alerts.get("results")) - len( mocked_alerts_page_1.get("results") ) output, last_run_new_dict = fetch_alerts_command(client, additional_alerts_amount, last_run) assert len(output) == additional_alerts_amount expected_ids = [str(i) for i in range(1, 9)] last_page_alerts_ids = last_run_new_dict.get("last_page_alerts_ids") assert set(last_page_alerts_ids) == set(expected_ids) # checks for duplicates for event in output: event_id = event.get("id") assert event_id not in seen_ids seen_ids.add(event_id) @pytest.mark.parametrize( "fetch_limit, expected_event_count", [ (12, 11), # Case: fetch_limit > available events (8, 8), # Case: fetch_limit < available events ], ) def test_fetch_event_type(mocker, fetch_limit, expected_event_count): """ Given: A mock MongoDB Atlas client with a single page of event data. When: Running fetch_event_type with different fetch limits. Then: Ensure that the number of events returned matches the expected count, and the min_time in last_run is updated to the lasted creation time. """ from MongoDBAtlasEventCollector import fetch_events_command mocked_events_page_1 = util_load_json("test_data/raw_events_page_1.json") mocker.patch("MongoDBAtlasEventCollector.Client.get_events_request", return_value=mocked_events_page_1) mocker.patch("MongoDBAtlasEventCollector.get_page_url", return_value=None) client = create_client() last_run = {"min_time": "2024-11-05T11:10:01Z", "events_with_created_min_time": []} output, last_run_new_dict = fetch_events_command(client, fetch_limit, last_run) assert len(output) == expected_event_count assert last_run_new_dict.get("min_time") is output[expected_event_count - 1].get("created") def test_fetch_event_type_min_time_repeat(mocker): """ Given: A mock MongoDB Atlas client with event data that includes duplicate timestamps for event creation. When: Running fetch_event_type with a set fetch limit, where events initially fetched share the same min_time as new events in a subsequent fetch. Then: Ensure that events are retrieved up to the fetch limit, min_time is updated appropriately after each fetch, and no duplicate event IDs are present in the final output. """ from MongoDBAtlasEventCollector import fetch_events_command raw_events_page_duplicated_dates = util_load_json("test_data/raw_events_page_duplicated_dates.json") mocker.patch("MongoDBAtlasEventCollector.Client.get_events_request", return_value=raw_events_page_duplicated_dates) mocker.patch("MongoDBAtlasEventCollector.get_page_url", return_value=None) client = create_client() last_run = {"min_time": "2024-11-05T11:00:01Z", "events_with_created_min_time": []} output, last_run_new_dict = fetch_events_command(client, 4, last_run) events_with_created_min_time = last_run_new_dict.get("events_with_created_min_time") min_time = last_run_new_dict.get("min_time") assert len(output) == 4 assert min_time == "2024-11-05T11:10:01Z" first_fetch_events_with_created_min_time = copy.deepcopy(events_with_created_min_time) last_run = {"min_time": min_time, "events_with_created_min_time": events_with_created_min_time} output, last_run_new_dict = fetch_events_command(client, 10, last_run) min_time = last_run_new_dict.get("min_time") assert len(output) == 10 assert min_time == "2024-11-10T14:21:28Z" for event_id in first_fetch_events_with_created_min_time: for event in output: assert event_id != event.get("id") @pytest.mark.parametrize( "fetch_limit, expected_event_count", [ (20, 20), # Case: fetch_limit < available events (25, 22), # Case: fetch_limit > available events ], ) def test_fetch_event_type_using_previous_page(mocker, fetch_limit, expected_event_count): """ Given: A mock MongoDB Atlas client with a fetch limit and paginated event data spread across 2 pages. When: Running fetch_event_type with a specified fetch limit and using previous page retrieval. Then: Ensure that the total number of events matches the expected count, min_time is updated based on the last event's created time, and no duplicate event IDs are present in the output. """ from MongoDBAtlasEventCollector import fetch_events_command raw_events_page_1 = util_load_json("test_data/raw_events_page_1.json") raw_events_page_2 = util_load_json("test_data/raw_events_page_2.json") mocker.patch("MongoDBAtlasEventCollector.Client.get_events_request", return_value=raw_events_page_2) mocker.patch("MongoDBAtlasEventCollector.get_page_url", side_effect=[None, True, False]) mocker.patch("MongoDBAtlasEventCollector.Client.get_response_from_page_link", return_value=raw_events_page_1) client = create_client() last_run = {"min_time": "2024-01-01T11:10:01Z", "events_with_created_min_time": []} output, last_run_new_dict = fetch_events_command(client, fetch_limit, last_run) assert len(output) == expected_event_count assert last_run_new_dict.get("min_time") is output[-1].get("created") # checks for duplicates seen_ids = set() for event in output: event_id = event.get("id") assert event_id not in seen_ids seen_ids.add(event_id) @pytest.mark.parametrize( "fetch_limit, mock_side_effect, expected_length, expected_last_id", [ # Case 1: Fetch limit within one page (30, [{"results": [{"id": i} for i in range(50)]}], 30, 29), # Case 2: Fetch limit across multiple pages ( 120, [ {"results": [{"id": i} for i in range(50)]}, {"results": [{"id": i} for i in range(50, 100)]}, {"results": [{"id": i} for i in range(100, 150)]}, ], 120, 119, ), # Case 3: Last page contains fewer items than requested ( 300, [ {"results": [{"id": i} for i in range(50)]}, {"results": [{"id": i} for i in range(50, 100)]}, {"results": [{"id": i} for i in range(100, 150)]}, {"results": [{"id": i} for i in range(150, 200)]}, {"results": [{"id": i} for i in range(200, 250)]}, {"results": []}, ], 250, 249, ), ( 125, [ {"results": [{"id": i} for i in range(50)]}, {"results": [{"id": i} for i in range(50, 100)]}, {"results": [{"id": i} for i in range(100, 110)]}, {"results": []}, ], 110, 109, ), ], ) def test_get_events_first_time_events(mocker, fetch_limit, mock_side_effect, expected_length, expected_last_id): """ Given: A mock MongoDB Atlas client with paginated event data. When: Running get_events_first_time_events with fetch limit to test retrieval within a single page, across multiple pages, and beyond available data. Then: Verify that: - The number of events returned matches the specified fetch limit, unless it exceeds available data. - The last event ID in the results aligns with the expected ID based on the fetch limit. - No duplicate events are returned. """ from MongoDBAtlasEventCollector import Client mocker.patch.object(Client, "get_events_request", side_effect=mock_side_effect) client = create_client() results = client.get_events_first_run(fetch_limit) assert len(results) == expected_length assert results[-1]["id"] == expected_last_id