RecordedFutureEventCollector
This integration fetches alerts from Recorded Future.
Analytics & SIEM · Recorded Future Intelligence
Details
| ID | RecordedFutureEventCollector |
|---|---|
| Provider | Mastercard |
| Category | Analytics & SIEM |
| From Version | 6.8.0 |
| Docker Image | demisto/python3:3.12.13.10116658 |
| Supported Modules | Agentix XSIAM |
README
This integration fetches alerts from Recorded Future.
This integration was integrated and tested with version 2 of the Recorded Future API.
This is the default integration for this content pack when configured by the Data Onboarder in Cortex XSIAM.
Configure Recorded Future Event Collector in Cortex
| Parameter | Description | Required |
|---|---|---|
| API token | The API token to use for the connection. | True |
| Trust any certificate (not secure) | Use SSL secure connection or not. | False |
| Use system proxy settings | Use proxy settings for connection or not. | False |
| First fetch time | First fetch query <number> <time unit>, e.g., 7 days. Default 3 days. |
False |
| Max fetch | The maximum number of events per fetch. Default and maximum is 1000. | 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.
recorded-future-get-events
Gets events from Recorded Future.
Base Command
recorded-future-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. Default is 10. | Optional |
Context Output
There is no context output for this command.
Configuration parameters
credentials— (required)insecure— Trust any certificate (not secure)proxy— Use system proxy settingsfirst_fetch— First fetch timemax_fetch— The maximum number of events per fetch. Default and maximum is 1000.
Commands (1)
-
recorded-future-get-eventsGets events from Recorded Future.
from CommonServerPython import * import json import urllib3 from typing import Any # Disable insecure warnings urllib3.disable_warnings() """ CONSTANTS """ BASE_URL = "https://api.recordedfuture.com/gw/xsiam" STATUS_TO_RETRY = [500, 501, 502, 503, 504] DATE_FORMAT = "%Y-%m-%dT%H:%M:%S.%fZ" VENDOR = "Recorded Future" PRODUCT = "Intelligence Cloud" """ CLIENT CLASS """ class Client(BaseClient): def _call(self, url_suffix, **kwargs): request_kwargs = { "method": "get", "url_suffix": url_suffix, "timeout": 90, "retries": 3, "status_list_to_retry": STATUS_TO_RETRY, } request_kwargs.update(kwargs) return self._http_request(**request_kwargs) def test_connection(self) -> dict[str, Any]: """Check connection.""" return self._call(url_suffix="/config/info") def get_alerts(self, params: dict = None) -> dict[str, Any]: """Get alerts.""" return self._call(url_suffix="/alert/search", params=params) """ COMMAND FUNCTIONS """ def test_module(client: Client): """ Tests API connectivity and authentication' When 'ok' is returned it indicates the integration works like it is supposed to and connection to the service is successful. Raises exceptions if something goes wrong. Args: client (Client): RecordedFuture client to use. Returns: str: 'ok' if test passed, anything else will raise an exception and will fail the test. """ try: client.test_connection() return_results("ok") except Exception as err: message = str(err) try: error = json.loads(str(err).split("\n")[1]) if "fail" in error.get("result", {}).get("status", ""): message = error.get("result", {})["message"] except Exception: message = f"Unknown error. Please verify that the API URL and Token are correctly configured. RAW Error: {err}" raise DemistoException(f"Failed due to - {message}") def get_events(client, params: dict) -> list: """ Retrieves events using the RecordedFuture API. Args: client (Client): RecordedFuture client to use. params (dict): The params to send to the API basically contains the limit for example {'limit': '1000'}. Returns: list: (list) of events that will be created in XSIAM. """ result = client.get_alerts(params) events = result.get("data", []) hr = tableToMarkdown(name="Test Event", t=events) return_results(CommandResults(readable_output=hr, raw_response=events)) return events def get_triggered(event: dict) -> str: """Get the 'triggered' value from an event without milliseconds since the API ignores them. Args: event (dict): The event from API. Returns: str: the "triggered" value. """ if event: return event.get("log", {}).get("triggered", "").split(".")[0] return "" def fetch_events(client: Client, **kwargs) -> tuple[list, dict]: """ Args: client (Client): RecordedFuture client to use. Returns: list: (list) of events that will be created in XSIAM. dict: The lastRun object to save for next run. """ params = { "triggered": f'[{kwargs.get("last_run")},]', "orderby": "triggered", "direction": "asc", "limit": kwargs.get("limit"), } response = client.get_alerts(params) next_run = {} if events := response.get("data", []): # Obtain the latest triggered time (for the next fetch round) next_run_time = get_triggered(events[0]) # We need the IDs of the events with the same trigger time as the latest, # So that we can remove them in the next fetch, Since we are fetching from (including) this time. next_run_ids = {event.get("id") for event in events if get_triggered(event) == next_run_time} # In case all events were triggered at the same time and the limit equals their amount, # We should increase the next run time, Otherwise the fetch will get stuck at this time forever. if len(next_run_ids) == int(kwargs.get("limit")): # type: ignore next_run_time = (datetime.strptime(next_run_time, DATE_FORMAT) + timedelta(seconds=1)).strftime(DATE_FORMAT) # Filter out events that have already been fetched. if last_run_event_ids := demisto.getLastRun().get("last_run_ids"): demisto.info(f"this is the last_run_event_ids {last_run_event_ids}") events = list(filter(lambda x: x.get("id") not in last_run_event_ids, events)) next_run = {"last_run_time": next_run_time, "last_run_ids": list(next_run_ids)} return events, next_run """ HELPER FUNCTIONS """ def add_time_key_to_events(events: list = None): """ Adds the _time key to the events. Args: events: list, the events to add the time key to. """ for event in events or []: event["_time"] = demisto.get(event, "log.triggered") """ MAIN FUNCTION """ def main() -> None: """ main function, parses params and runs command functions """ params = demisto.params() args = demisto.args() command = demisto.command() verify_certificate = not params.get("insecure", False) proxy = params.get("proxy", False) api_key = params.get("credentials", {}).get("password") headers = {"X-RFToken": api_key} demisto.info(f"Command being called is {command}") try: client = Client(base_url=BASE_URL, headers=headers, verify=verify_certificate, proxy=proxy) if command == "test-module": test_module(client) elif command == "recorded-future-get-events": events = get_events(client, params={"limit": args.get("limit", 10)}) if argToBoolean(args.get("should_push_events", False)): add_time_key_to_events(events) send_events_to_xsiam(events, vendor=VENDOR, product=PRODUCT) elif command == "fetch-events": if not (last_run := demisto.getLastRun().get("last_run_time")): last_run = arg_to_datetime(params.get("first_fetch", "3 days")).strftime(DATE_FORMAT) # type: ignore events, next_run = fetch_events( client=client, limit=args.get("limit") or params.get("max_fetch") or 1000, last_run=last_run ) add_time_key_to_events(events) send_events_to_xsiam(events, vendor=VENDOR, product=PRODUCT) if next_run: demisto.setLastRun(next_run) # Log exceptions and return errors except Exception as e: return_error(f"Failed to execute {command} command.\nError:\n{str(e)}") """ ENTRY POINT """ if __name__ in ("__main__", "__builtin__", "builtins"): main()