Kibana
This integration enables using Elastic Security for SIEM for security operations management and searching Elastic logs. This pack is to be used in combination with the Elasticsearch v2 integration.
Analytics & SIEM · Kibana
Details
| ID | Kibana |
|---|---|
| Provider | Elastic |
| Category | Analytics & SIEM |
| From Version | 6.0.0 |
| Docker Image | demisto/elasticsearch:1.0.0.10133006 |
README
Use the Kibana integration to manage Elastic Security cases, detection alerts, rules, and value lists for security operations.
This integration was tested with Elasticsearch versions 6.6.2, 7.3, 8.4.1, and 9.3.1.
Configure Kibana in Cortex
| Parameter | Description | Required |
|---|---|---|
| Server URL | The Elasticsearch server to which the integration connects. Ensure that the URL includes the correct Elasticsearch port. The default port for Elasticsearch v7 and below is 9200. Use the Server URL for on-premises deployments. | True |
| Elastic API Port | The port for the Elastic API. | False |
| Kibana API Port | The port for the Kibana API. | False |
| Authorization type | The authentication type and credentials to use: Basic Auth (Username and Password), Bearer Auth (Username and Password), or API Key Auth (API Key ID and API Key). | False |
| API key ID | False | |
| API Key | False | |
| Username | The username and password to use instead of API key and API ID. | False |
| Password | False | |
| Trust any certificate (not secure) | False | |
| Use system proxy settings | False | |
| Client type | In some hosted ElasticSearch environments, the standard ElasticSearch client is not supported. If you encounter any related client issues, please consider using the OpenSearch client type. | False |
| Request timeout (in seconds). | 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.
kibana-cases-find
Lists cases in Kibana.
Base Command
kibana-cases-find
Input
| Argument Name | Description | Required |
|---|---|---|
| status | The status of the cases to retrieve. Possible values are: open, in-progress, closed. Default is open. | Optional |
| severity | The severity of the cases to retrieve. Possible values are: critical, high, medium, low. | Optional |
| from_time | The earliest time to search from (for example, 2025-10-02T00:27:58.162Z). | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Kibana.Cases.Status | unknown | The status of the case in Kibana. |
| Kibana.Cases.Version | unknown | The version number of the case in Kibana. |
| Kibana.Cases.ID | unknown | The ID number of the case in Kibana. |
kibana-case-alerts-find
Returns information on the alerts of the input case in Kibana.
Base Command
kibana-case-alerts-find
Input
| Argument Name | Description | Required |
|---|---|---|
| case_id | The ID of the case in Kibana. Locate it with the “kibana-cases-find” command. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| Kibana.CaseAlerts.ID | unknown | The ID of alerts tied to the case in Kibana. |
kibana-alert-status-update
Updates the status of an input alert.
Base Command
kibana-alert-status-update
Input
| Argument Name | Description | Required |
|---|---|---|
| alert_id | The alert ID to update. Find it with the “kibana-detection-alerts-list” command. | Required |
| status | The status to set the alert to. Possible values are: open, closed. | Required |
Context Output
There is no context output for this command.
kibana-case-status-update
Updates the status of an input case.
Base Command
kibana-case-status-update
Input
| Argument Name | Description | Required |
|---|---|---|
| status | The status of the case to update. Possible values are: open, in-progress, closed. | Required |
| case_id | The ID of the case in Kibana. Locate it with the “kibana-cases-find” command. | Required |
| version_id | The version ID of the case. Find it with the “kibana-cases-find” command. This ID changes after each case update. | Required |
Context Output
There is no context output for this command.
kibana-user-spaces-find
Gets the list of user spaces in Kibana.
Base Command
kibana-user-spaces-find
Input
| Argument Name | Description | Required |
| — | — | — |
Context Output
| Path | Type | Description |
|---|---|---|
| Kibana.UserSpaces.description | unknown | The default user space description. |
| Kibana.UserSpaces.disabledFeatures | unknown | The list of disabled Kibana features. |
kibana-case-comments-find
Finds comments for an input case ID.
Base Command
kibana-case-comments-find
Input
| Argument Name | Description | Required |
|---|---|---|
| case_id | The case ID to find comments for. Locate it with the “kibana-cases-find” command. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| Kibana.CaseComments.version | unknown | The version number of the case comment in Kibana. |
| Kibana.CaseComments.id | unknown | The ID number of the case comment in Kibana. |
kibana-case-delete
Deletes a case in Kibana based on case ID.
Base Command
kibana-case-delete
Input
| Argument Name | Description | Required |
|---|---|---|
| case_id | The case ID to delete. Locate it with the “kibana-cases-find” command. | Required |
Context Output
There is no context output for this command.
kibana-rule-delete
Deletes a rule in Kibana based on the input rule ID.
Base Command
kibana-rule-delete
Input
| Argument Name | Description | Required |
|---|---|---|
| rule_id | The rule ID to delete. Find it with the “kibana-rule-details-search” command. | Required |
Context Output
There is no context output for this command.
kibana-rule-details-search
Retrieves details about a detection rule in Kibana based on the input KQL filter.
Base Command
kibana-rule-details-search
Input
| Argument Name | Description | Required |
|---|---|---|
| kql_query | The KQL filter to search rules with. For example: “alert.attributes.name: Smith”. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Kibana.RuleDetails.enabled | unknown | Whether the rule is enabled in Kibana. |
| Kibana.RuleDetails.name | unknown | The name of the rule in Kibana. |
| Kibana.RuleDetails.id | unknown | The ID of the rule in Kibana. |
kibana-case-comment-add
Adds a comment to a case in Kibana. The case ID and owner can be obtained from the “kibana-cases-find” command.
Base Command
kibana-case-comment-add
Input
| Argument Name | Description | Required |
|---|---|---|
| case_id | The case ID to add the comment to. Locate it with the “kibana-cases-find” command. | Required |
| case_owner | The owner of the case, as listed in the “kibana-cases-find” command output. Possible values are: cases, observability, securitySolution. | Required |
| comment | The comment to add to the case in Kibana. | Required |
Context Output
There is no context output for this command.
kibana-user-list-get
Searches for the list of users in Kibana and returns the users’ UIDs.
Base Command
kibana-user-list-get
Input
| Argument Name | Description | Required |
| — | — | — |
Context Output
| Path | Type | Description |
|---|---|---|
| Kibana.UserList.username | unknown | The username of the user in Kibana. |
| Kibana.UserList.roles | unknown | The associated roles of the user in Kibana. |
kibana-alert-assign
Assigns an alert in Kibana to a user via user ID input.
Base Command
kibana-alert-assign
Input
| Argument Name | Description | Required |
|---|---|---|
| user_id | The UID of the user to be assigned. Locate it with the “kibana-user-list-get” command. | Required |
| alert_id | The alert ID to assign the user to. Find it with the “kibana-detection-alerts-list” command. | Required |
Context Output
There is no context output for this command.
kibana-detection-alerts-list
Searches for detection alerts in Kibana.
Base Command
kibana-detection-alerts-list
Input
| Argument Name | Description | Required |
|---|---|---|
| alert_status | The status of the detection alert to search for. Possible values are: open, closed. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| Kibana.DetectionAlerts.bhe.windows.security_id | unknown | The username associated with the detection alert. |
| Kibana.DetectionAlerts.kibana.alert.original_data_stream.dataset | unknown | The dataset associated with the detection alert. |
| Kibana.DetectionAlerts.message | unknown | The raw log message of the detection alert. |
| Kibana.DetectionAlerts.kibana.alert.uuid | unknown | The ID of the detection alert. |
| Kibana.DetectionAlerts.kibana.alert.rule.name | unknown | The rule name associated with the detection alert. |
kibana-alert-note-add
Adds a note to an alert in Kibana.
Base Command
kibana-alert-note-add
Input
| Argument Name | Description | Required |
|---|---|---|
| alert_id | The alert ID to update the note on. Find it with the “kibana-detection-alerts-list” command. | Required |
| note | The note text to add to the alert. | Required |
Context Output
There is no context output for this command.
kibana-alerting-health-get
Retrieves the health status of the Kibana alerting framework.
Base Command
kibana-alerting-health-get
Input
| Argument Name | Description | Required |
| — | — | — |
Context Output
| Path | Type | Description |
|---|---|---|
| Kibana.AlertingFrameworkHealth.alerting_framework_health.decryption_health.status | unknown | Whether Kibana can successfully decrypt encrypted alert data. |
| Kibana.AlertingFrameworkHealth.alerting_framework_health.execution_health.status | unknown | Whether rules are running on time or failing. |
| Kibana.AlertingFrameworkHealth.alerting_framework_health.read_health.status | unknown | Whether rule configurations can be successfully retrieved from internal Kibana indices. |
kibana-alert-rule-disable
Disables a detection alerting rule. Clears associated alerts from the active alerts page.
Base Command
kibana-alert-rule-disable
Input
| Argument Name | Description | Required |
|---|---|---|
| rule_id | The rule ID to disable. Find it with the “kibana-rule-details-search” command. | Required |
Context Output
There is no context output for this command.
kibana-alert-rule-enable
Enables a rule used for detection alerting.
Base Command
kibana-alert-rule-enable
Input
| Argument Name | Description | Required |
|---|---|---|
| rule_id | The rule ID to enable. Find it with the “kibana-rule-details-search” command. | Required |
Context Output
There is no context output for this command.
kibana-exception-lists-get
Retrieves a list of all exception list containers.
Base Command
kibana-exception-lists-get
Input
| Argument Name | Description | Required |
| — | — | — |
Context Output
| Path | Type | Description |
|---|---|---|
| Kibana.ExceptionLists.name | unknown | The name of the exception list. |
| Kibana.ExceptionLists.list_id | unknown | The list ID of the exception list. |
| Kibana.ExceptionLists.description | unknown | The description of the exception list. |
kibana-value-list-create
Creates a value list in Kibana.
Base Command
kibana-value-list-create
Input
| Argument Name | Description | Required |
|---|---|---|
| description | The description of the value list. | Required |
| name | The name of the value list. | Required |
| data_type | The Elasticsearch data type the list container holds. Possible values are: keyword, ip, ip_range, text. | Required |
| list_id | The identifier of the value list. | Required |
Context Output
There is no context output for this command.
kibana-value-lists-get
Finds all value lists in the Kibana Detection Rules menu.
Base Command
kibana-value-lists-get
Input
| Argument Name | Description | Required |
| — | — | — |
Context Output
| Path | Type | Description |
|---|---|---|
| Kibana.ValueLists.name | unknown | The name of the value list. |
| Kibana.ValueLists.id | unknown | The ID of the value list. |
| Kibana.ValueLists.description | unknown | The description of the value list. |
kibana-value-list-items-import
Imports value list items from a TXT or CSV file.
Base Command
kibana-value-list-items-import
Input
| Argument Name | Description | Required |
|---|---|---|
| list_id | The value list ID to import values to. Find it with the “kibana-value-lists-get” command. | Required |
| file_content | The IOC file entries to import to Kibana in Python string format. | Required |
Context Output
There is no context output for this command.
kibana-value-list-item-create
Creates a value list item and associates it with the specified value list.
Base Command
kibana-value-list-item-create
Input
| Argument Name | Description | Required |
|---|---|---|
| list_id | The value list ID to update. Find it with the “kibana-value-lists-get” command. | Required |
| new_value_list_item | The item to add to the specified value list. | Required |
Context Output
There is no context output for this command.
kibana-value-list-items-get
Displays entries in an input value list.
Base Command
kibana-value-list-items-get
Input
| Argument Name | Description | Required |
|---|---|---|
| list_id | The value list ID to retrieve values for. Find it with the “kibana-value-lists-get” command. | Required |
| result_size | The size of results to return. Default is 100. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Kibana.ValueListItems.value | unknown | The value of the value list item. |
| Kibana.ValueListItems.id | unknown | The ID of the value list item. |
| Kibana.ValueListItems.list_id | unknown | The list ID of the value list. |
kibana-value-list-item-delete
Deletes a value list item, given the item ID and list ID as input.
Base Command
kibana-value-list-item-delete
Input
| Argument Name | Description | Required |
|---|---|---|
| item_id | The value list entry ID to delete. Find it with the “kibana-value-list-items-get” command. | Required |
| list_id | The value list ID to delete the value from. Find it with the “kibana-value-lists-get” command. | Required |
Context Output
There is no context output for this command.
kibana-value-list-delete
Deletes a value list given the list ID as input.
Base Command
kibana-value-list-delete
Input
| Argument Name | Description | Required |
|---|---|---|
| list_id | The value list ID to delete. Find it with the “kibana-value-lists-get” command. | Required |
Context Output
There is no context output for this command.
kibana-status-get
Checks the Kibana operational status.
Base Command
kibana-status-get
Input
| Argument Name | Description | Required |
| — | — | — |
Context Output
| Path | Type | Description |
|---|---|---|
| Kibana.OperationalStatus.core.elasticsearch.level | unknown | The connection health between Kibana and Elasticsearch. |
| Kibana.OperationalStatus.overall.level | unknown | The aggregated health status of the Kibana instance. |
| Kibana.OperationalStatus.core.savedObjects.level | unknown | The health status of the Saved Objects repository. |
kibana-task-manager-health-get
Retrieves the health status of the Kibana task manager.
Base Command
kibana-task-manager-health-get
Input
| Argument Name | Description | Required |
| — | — | — |
Context Output
| Path | Type | Description |
|---|---|---|
| Kibana.TaskManagerHealth.capacity_estimation.status | unknown | The ability to handle scheduled tasks in Kibana. |
| Kibana.TaskManagerHealth.configuration.status | unknown | The configuration status of the Kibana task manager. |
| Kibana.TaskManagerHealth.runtime.status | unknown | The performance, drift, and load of Kibana task execution. |
| Kibana.TaskManagerHealth.workload.status | unknown | The status of tasks running, to identify potential overload. |
kibana-upgrade-readiness-status-get
Checks the status of the cluster.
Base Command
kibana-upgrade-readiness-status-get
Input
| Argument Name | Description | Required |
| — | — | — |
Context Output
| Path | Type | Description |
|---|---|---|
| Kibana.UpgradeReadinessStatus.details | unknown | The details for what is needed prior to Kibana upgrades. |
| Kibana.UpgradeReadinessStatus.readyForUpgrade | unknown | Whether Kibana is ready for upgrade. |
kibana-case-comment-delete
Deletes a case comment.
Base Command
kibana-case-comment-delete
Input
| Argument Name | Description | Required |
|---|---|---|
| case_id | The case ID to delete the comment on. Retrieve case IDs with the “kibana-cases-find” command. | Required |
| comment_id | The identifier for the comment. Find comment IDs with the “kibana-case-comments-find” command. | Required |
Context Output
There is no context output for this command.
kibana-case-file-add
Attaches a file to a case.
Base Command
kibana-case-file-add
Input
| Argument Name | Description | Required |
|---|---|---|
| case_id | The case ID to attach the file to. Locate it with the “kibana-cases-find” command. | Required |
| file_id | The file entry ID from Cortex XSOAR context data to add to the case. | Required |
Context Output
There is no context output for this command.
kibana-user-by-email-get
Searches for a single user’s UID in Kibana by email address filter.
Base Command
kibana-user-by-email-get
Input
| Argument Name | Description | Required |
|---|---|---|
| email_wildcard | The full or partial email address to search for the user with (for example, william.smith@*). | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| Kibana.UserData.profile_uid | unknown | The user ID for tracking user activity and checking privileges. |
| Kibana.UserData.roles | unknown | The roles tied to the user account. |
kibana-case-information-get
Retrieves information for a specific case in Kibana.
Base Command
kibana-case-information-get
Input
| Argument Name | Description | Required |
|---|---|---|
| case_id | The case ID to retrieve information for. View available case IDs with the “kibana-cases-find” command. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| Kibana.CaseInfo.status | unknown | Whether the case is open, in-progress, or closed. |
| Kibana.CaseInfo.owner | unknown | The application that created the case. |
| Kibana.CaseInfo.version | unknown | The version of the case being updated. |
| Kibana.CaseInfo.id | unknown | The unique identifier for a case. |
Configuration parameters
url— Server URL (required)elastic_port— Elastic API Portkibana_port— Kibana API Portauth_type— Authorization typeapi_key_auth_credentials— API key IDcredentials— Usernameinsecure— Trust any certificate (not secure)proxy— Use system proxy settingsclient_type— Client typetimeout— Request timeout (in seconds).
Commands (32)
-
kibana-alert-assignAssigns an alert in Kibana to a user via user ID input.
-
kibana-alert-note-addAdds a note to an alert in Kibana.
-
kibana-alert-rule-disableDisables a detection alerting rule. Clears associated alerts from the active alerts page.
-
kibana-alert-rule-enableEnables a rule used for detection alerting.
-
kibana-alert-status-updateUpdates the status of an input alert.
-
kibana-alerting-health-getRetrieves the health status of the Kibana alerting framework.
-
kibana-case-alerts-findReturns information on the alerts of the input case in Kibana.
-
kibana-case-comment-addAdds a comment to a case in Kibana. The case ID and owner can be obtained from the "kibana-cases-find" command.
-
kibana-case-comment-deleteDeletes a case comment.
-
kibana-case-comments-findFinds comments for an input case ID.
-
kibana-case-deleteDeletes a case in Kibana based on case ID.
-
kibana-case-file-addAttaches a file to a case.
-
kibana-case-information-getRetrieves information for a specific case in Kibana.
-
kibana-case-status-updateUpdates the status of an input case.
-
kibana-cases-findLists cases in Kibana.
-
kibana-detection-alerts-listSearches for detection alerts in Kibana.
-
kibana-exception-lists-getRetrieves a list of all exception list containers.
-
kibana-rule-deleteDeletes a rule in Kibana based on the input rule ID.
-
kibana-rule-details-searchRetrieves details about a detection rule in Kibana based on the input KQL filter.
-
kibana-status-getChecks the Kibana operational status.
-
kibana-task-manager-health-getRetrieves the health status of the Kibana task manager.
-
kibana-upgrade-readiness-status-getChecks the status of the cluster.
-
kibana-user-by-email-getSearches for a single user's UID in Kibana by email address filter.
-
kibana-user-list-getSearches for the list of users in Kibana and returns the users' UIDs.
-
kibana-user-spaces-findGets the list of user spaces in Kibana.
-
kibana-value-list-createCreates a value list in Kibana.
-
kibana-value-list-deleteDeletes a value list given the list ID as input.
-
kibana-value-list-item-createCreates a value list item and associates it with the specified value list.
-
kibana-value-list-item-deleteDeletes a value list item, given the item ID and list ID as input.
-
kibana-value-list-items-getDisplays entries in an input value list.
-
kibana-value-list-items-importImports value list items from a TXT or CSV file.
-
kibana-value-lists-getFinds all value lists in the Kibana Detection Rules menu.
"""Unit tests for the Kibana integration.""" import base64 import importlib import sys from datetime import datetime, timedelta, timezone from types import ModuleType from unittest.mock import MagicMock import demistomock as demisto import pytest UTC = timezone.utc # noqa: UP017 DEFAULT_PARAMS = { "url": "https://example.com", "elastic_port": "9200", "kibana_port": "443", "auth_type": "Basic auth", "credentials": {"identifier": "user", "password": "pass"}, "api_key_auth_credentials": {"identifier": "key_id", "password": "key_secret"}, "client_type": "Elasticsearch", "insecure": False, "timeout": "60", "proxy": False, } def _stub_elasticsearch_modules(): """Stub the optional elasticsearch/opensearch clients so the module imports without them installed.""" for name in ("elasticsearch", "elasticsearch7", "opensearchpy", "elastic_transport"): module = ModuleType(name) # Provide the symbols imported by Kibana.py at module load time. module.Elasticsearch = MagicMock() # type: ignore[attr-defined] module.OpenSearch = MagicMock() # type: ignore[attr-defined] module.RequestsHttpConnection = MagicMock() # type: ignore[attr-defined] module.RequestsHttpNode = object # type: ignore[attr-defined] sys.modules.setdefault(name, module) @pytest.fixture() def kibana(mocker): """Import the Kibana module with mocked params and stubbed clients.""" _stub_elasticsearch_modules() mocker.patch.object(demisto, "params", return_value=dict(DEFAULT_PARAMS)) if "Kibana" in sys.modules: module = importlib.reload(sys.modules["Kibana"]) else: module = importlib.import_module("Kibana") return module def test_port_fallback_defaults(mocker): """ Given: params without explicit ports. When: the module is imported. Then: ELASTIC_SERVER/KIBANA_SERVER fall back to 9200/443 (no TypeError). """ _stub_elasticsearch_modules() params = dict(DEFAULT_PARAMS) params["elastic_port"] = "" params["kibana_port"] = "" mocker.patch.object(demisto, "params", return_value=params) module = importlib.reload(sys.modules["Kibana"]) if "Kibana" in sys.modules else importlib.import_module("Kibana") assert module.ELASTIC_SERVER.endswith(":9200") assert module.KIBANA_SERVER.endswith(":443") def test_get_api_key_header_val_with_tuple(kibana): """ Given: an API key as a (id, secret) tuple. When: building the ApiKey header value. Then: it returns a base64-encoded "ApiKey ..." string. """ result = kibana.get_api_key_header_val(("my_id", "my_secret")) expected = "ApiKey " + base64.b64encode(b"my_id:my_secret").decode() assert result == expected def test_get_api_key_header_val_with_string(kibana): """ Given: an API key already encoded as a string. When: building the ApiKey header value. Then: it is returned as-is with the ApiKey prefix. """ assert kibana.get_api_key_header_val("encoded_key") == "ApiKey encoded_key" def test_is_access_token_expired_valid(kibana): """ Given: an expiration time well in the future. When: checking whether the token is expired. Then: it returns False. """ future = (datetime.now(UTC) + timedelta(hours=1)).strftime("%Y-%m-%dT%H:%M:%SZ") assert kibana.is_access_token_expired(future) is False def test_is_access_token_expired_past(kibana): """ Given: an expiration time in the past. When: checking whether the token is expired. Then: it returns True. """ past = (datetime.now(UTC) - timedelta(hours=1)).strftime("%Y-%m-%dT%H:%M:%SZ") assert kibana.is_access_token_expired(past) is True def test_is_access_token_expired_invalid(kibana): """ Given: a malformed expiration string. When: checking whether the token is expired. Then: it is treated as expired (returns True). """ assert kibana.is_access_token_expired("not-a-date") is True def test_verify_es_server_version_v8_with_v7_client_raises(kibana): """ Given: an ES v8 server while the configured client is the legacy v7 'Elasticsearch'. When: verifying the server version. Then: a configuration ValueError is raised. """ with pytest.raises(ValueError, match="Configuration Error"): kibana.verify_es_server_version({"version": {"number": "8.4.1"}}) def test_verify_es_server_version_v7_ok(kibana): """ Given: an ES v7 server with the default 'Elasticsearch' client. When: verifying the server version. Then: no exception is raised. """ kibana.verify_es_server_version({"version": {"number": "7.3.0"}}) def test_kibana_find_cases_uses_arg_to_datetime_and_raw_response(kibana, mocker): """ Given: a from_time argument and a successful API response. When: kibana_find_cases is called. Then: from_time is normalized to ISO format, and the CommandResults includes outputs and raw_response. """ api_response = {"cases": [{"id": "1", "status": "open"}]} http_mock = mocker.patch.object(kibana, "http_request", return_value=api_response) result = kibana.kibana_find_cases({"status": "open", "from_time": "2025-10-02T00:00:00Z"}, proxies=None) assert result.outputs == api_response["cases"] assert result.raw_response == api_response assert result.outputs_prefix == "Kibana.Cases" # arg_to_datetime should have converted from_time into the API "from" param. _, kwargs = http_mock.call_args assert kwargs["params"]["from"] is not None def test_kibana_update_alert_status_returns_command_results(kibana, mocker): """ Given: a successful update call. When: kibana_update_alert_status is called. Then: it returns a CommandResults (not a plain string) with the expected readable output. """ mocker.patch.object(kibana, "http_request", return_value={}) result = kibana.kibana_update_alert_status({"alert_id": "a1", "status": "closed"}, proxies=None) assert isinstance(result, kibana.CommandResults) assert result.readable_output == "Updated alert ID a1 to status of closed" def test_kibana_delete_case_serializes_ids_as_json(kibana, mocker): """ Given: a case_id to delete. When: kibana_delete_case is called. Then: the ids query param is a proper JSON-encoded list, and a CommandResults is returned. """ http_mock = mocker.patch.object(kibana, "http_request", return_value={}) result = kibana.kibana_delete_case({"case_id": "case-123"}, proxies=None) _, kwargs = http_mock.call_args assert kwargs["params"]["ids"] == '["case-123"]' assert isinstance(result, kibana.CommandResults) def test_kibana_get_user_list_handles_v8_body(kibana, mocker): """ Given: an ES v8 client response exposing a .body attribute. When: kibana_get_user_list is called. Then: the users are read from .body and returned in CommandResults. """ es_response = MagicMock() es_response.body = {"users": [{"username": "alice"}]} es = MagicMock() es.security.query_user.return_value = es_response mocker.patch.object(kibana, "elasticsearch_builder", return_value=es) result = kibana.kibana_get_user_list({}, proxies=None) assert result.outputs == [{"username": "alice"}] def test_kibana_get_user_list_handles_v7_dict(kibana, mocker): """ Given: an ES v7/OpenSearch client returning a plain dict (no .body). When: kibana_get_user_list is called. Then: the users are read directly from the dict (no AttributeError). """ es = MagicMock() es.security.query_user.return_value = {"users": [{"username": "bob"}]} mocker.patch.object(kibana, "elasticsearch_builder", return_value=es) result = kibana.kibana_get_user_list({}, proxies=None) assert result.outputs == [{"username": "bob"}] def test_test_func_success(kibana, mocker): """ Given: a successful connectivity/auth check. When: test_func is called. Then: it returns "ok". """ mocker.patch.object(kibana, "test_connectivity_auth", return_value=(True, "Connectivity test successful")) assert kibana.test_func(proxies=None) == "ok" def test_http_request_returns_json_on_success(kibana, mocker): """ Given: a server responding 200 with a JSON body. When: http_request is called with parse_json=True (default). Then: the parsed JSON is returned. """ response = MagicMock() response.status_code = 200 response.json.return_value = {"ok": True} mocker.patch.object(kibana.requests, "request", return_value=response) result = kibana.http_request(method="GET", url_suffix="/api/status", headers={}) assert result == {"ok": True} def test_http_request_returns_none_on_204(kibana, mocker): """ Given: a server responding 204 No Content. When: http_request is called. Then: None is returned (no JSON parsing attempted). """ response = MagicMock() response.status_code = 204 mocker.patch.object(kibana.requests, "request", return_value=response) assert kibana.http_request(method="DELETE", url_suffix="/api/lists", headers={}) is None def test_http_request_calls_return_error_on_failure(kibana, mocker): """ Given: a server responding 500 with a JSON error body. When: http_request is called. Then: return_error is invoked with the status code and reason. """ response = MagicMock() response.status_code = 500 response.json.return_value = {"message": "boom"} mocker.patch.object(kibana.requests, "request", return_value=response) return_error_mock = mocker.patch.object(kibana, "return_error") kibana.http_request(method="GET", url_suffix="/api/status", headers={}) return_error_mock.assert_called_once() assert "500" in return_error_mock.call_args[0][0] def test_test_connectivity_auth_basic_success(kibana, mocker): """ Given: Basic auth configured and a healthy v7 server. When: test_connectivity_auth is called. Then: it returns (True, "Connectivity test successful"). """ response = MagicMock() response.status_code = 200 response.json.return_value = {"version": {"number": "7.10.0"}} mocker.patch.object(kibana, "AUTH_TYPE", kibana.BASIC_AUTH) mocker.patch.object(kibana.requests, "get", return_value=response) success, message = kibana.test_connectivity_auth(proxies=None) assert success is True assert message == "Connectivity test successful" def test_test_connectivity_auth_failure_status(kibana, mocker): """ Given: Basic auth configured and a server returning 403. When: test_connectivity_auth is called. Then: it returns (False, message) describing the failure. """ response = MagicMock() response.status_code = 403 response.reason = "Forbidden" mocker.patch.object(kibana, "AUTH_TYPE", kibana.BASIC_AUTH) mocker.patch.object(kibana.requests, "get", return_value=response) success, message = kibana.test_connectivity_auth(proxies=None) assert success is False assert "Failed to connect" in message def test_test_func_failure(kibana, mocker): """ Given: a failed connectivity/auth check. When: test_func is called. Then: it returns the failure message. """ mocker.patch.object(kibana, "test_connectivity_auth", return_value=(False, "Failed to connect.")) assert kibana.test_func(proxies=None) == "Failed to connect." # Command functions that wrap http_request and return a CommandResults with a fixed readable_output. # Each entry: (function_name, args, http_response, expected_readable_output) SIMPLE_WRITE_COMMANDS = [ ("kibana_assign_alert_user", {"alert_id": "a1", "user_id": "u1"}, {}, "Assigned user ID u1 to alert a1"), ("kibana_add_alert_note", {"alert_id": "e1", "note": "hello"}, {}, "Added note hello to alert e1"), ("kibana_delete_rule", {"rule_id": "r1"}, {}, "Successfully deleted rule with ID of r1"), ("kibana_disable_alert_rule", {"rule_id": "r1"}, {}, "Successfully disabled rule with ID of r1"), ("kibana_enable_alert_rule", {"rule_id": "r1"}, {}, "Successfully enabled rule with ID of r1"), ( "kibana_create_value_list", {"description": "d", "list_id": "l1", "name": "n1", "data_type": "keyword"}, {}, "Successfully created value list with name of n1", ), ( "kibana_create_value_list_item", {"list_id": "l1", "new_value_list_item": "v1"}, {}, "Successfully added v1 to value list with ID of l1", ), ( "kibana_import_value_list_items", {"list_id": "l1", "file_content": "c1"}, {}, "Successfully imported c1 to value list with ID of l1", ), ( "kibana_delete_value_list_item", {"item_id": "i1", "list_id": "l1"}, {}, "Successfully deleted i1 from value list with ID of l1", ), ("kibana_delete_value_list", {"list_id": "l1"}, {}, "Successfully deleted value list with ID of l1"), ( "kibana_delete_case_comment", {"case_id": "c1", "comment_id": "cm1"}, {}, "Deleted comment with ID cm1 from case c1", ), ( "kibana_add_case_comment", {"case_id": "c1", "case_owner": "o", "comment": "txt"}, {"updated_at": "2025-01-01"}, "Case comment updated at 2025-01-01", ), ] @pytest.mark.parametrize("func_name, args, response, expected", SIMPLE_WRITE_COMMANDS) def test_simple_write_commands(kibana, mocker, func_name, args, response, expected): """ Given: a write-style command and a successful API response. When: the command function is called. Then: it returns a CommandResults with the expected human-readable output. """ mocker.patch.object(kibana, "http_request", return_value=response) result = getattr(kibana, func_name)(args, proxies=None) assert isinstance(result, kibana.CommandResults) assert result.readable_output == expected # Command functions that wrap http_request and surface (a slice of) the response as outputs. # Each entry: (function_name, args, http_response, expected_outputs, outputs_prefix) READ_COMMANDS = [ ( "kibana_find_alerts_for_case", {"case_id": "c1"}, {"alerts": [{"id": "x"}]}, {"alerts": [{"id": "x"}]}, "Kibana.Alerts.For.Case", ), ( "kibana_update_case_status", {"case_id": "c1", "status": "closed", "version_id": "v1"}, [{"id": "c1", "status": "closed"}], [{"id": "c1", "status": "closed"}], "Kibana.Updated.Case.Status", ), ( "kibana_find_user_spaces", {}, [{"id": "default"}], [{"id": "default"}], "Kibana.User.Spaces", ), ( "kibana_find_case_comments", {"case_id": "c1"}, {"comments": [{"id": "cm1"}]}, [{"id": "cm1"}], "Kibana.Case.Comments", ), ( "kibana_search_rule_details", {"kql_query": "name:*"}, {"data": [{"id": "r1"}]}, [{"id": "r1"}], "Kibana.Rule.Details", ), ( "kibana_get_alerting_health", {}, {"status": "ok"}, {"status": "ok"}, "Alerting.Framework.Health", ), ( "kibana_get_exception_lists", {}, {"data": [{"id": "el1"}]}, [{"id": "el1"}], "Kibana.Exception.Lists", ), ( "kibana_get_value_lists", {}, {"data": [{"id": "vl1"}]}, [{"id": "vl1"}], "Alerting.Value.Lists", ), ( "kibana_get_value_list_items", {"list_id": "l1", "result_size": "10"}, {"data": [{"value": "v1"}]}, [{"value": "v1"}], "Value.List.Items", ), ( "kibana_get_status", {}, {"status": {"overall": {"level": "available"}}}, {"overall": {"level": "available"}}, "Kibana.Operational.Status", ), ( "kibana_get_task_manager_health", {}, {"stats": {"runtime": {}}}, {"runtime": {}}, "Kibana.Task.Manager.Health", ), ( "kibana_get_upgrade_readiness_status", {}, {"readyForUpgrade": True}, {"readyForUpgrade": True}, "Kibana.Upgrade.Readiness.Status", ), ( "kibana_get_case_information", {"case_id": "c1"}, {"id": "c1", "title": "t"}, {"id": "c1", "title": "t"}, "Kibana.Case.Info", ), ] @pytest.mark.parametrize("func_name, args, response, expected_outputs, prefix", READ_COMMANDS) def test_read_commands(kibana, mocker, func_name, args, response, expected_outputs, prefix): """ Given: a read-style command and a successful API response. When: the command function is called. Then: it returns a CommandResults with the expected outputs and outputs_prefix. """ mocker.patch.object(kibana, "http_request", return_value=response) result = getattr(kibana, func_name)(args, proxies=None) assert isinstance(result, kibana.CommandResults) assert result.outputs == expected_outputs assert result.outputs_prefix == prefix def test_kibana_list_detection_alerts_flattens_sources(kibana, mocker): """ Given: a detection alerts search response with nested hits. When: kibana_list_detection_alerts is called. Then: each hit's _source is flattened into the outputs list. """ response = {"hits": {"hits": [{"_source": {"id": "1"}}, {"_source": {"id": "2"}}]}} mocker.patch.object(kibana, "http_request", return_value=response) result = kibana.kibana_list_detection_alerts({"alert_status": "open"}, proxies=None) assert result.outputs == [{"id": "1"}, {"id": "2"}] assert result.outputs_prefix == "Kibana.Detection.Alerts" def test_kibana_get_user_by_email_returns_users(kibana, mocker): """ Given: an email wildcard and an ES client returning matching users. When: kibana_get_user_by_email is called. Then: the users list is returned in CommandResults. """ es = MagicMock() es.security.query_user.return_value = {"users": [{"username": "carol"}]} mocker.patch.object(kibana, "elasticsearch_builder", return_value=es) result = kibana.kibana_get_user_by_email({"email_wildcard": "carol@*"}, proxies=None) assert result.outputs == [{"username": "carol"}] assert result.outputs_prefix == "Kibana.User.Data" def test_kibana_add_file_to_case_uploads_and_reports(kibana, mocker, tmp_path): """ Given: a file registered in the war room and a successful upload. When: kibana_add_file_to_case is called. Then: it uploads the file and returns a success message including the file name. """ file_path = tmp_path / "evidence.txt" file_path.write_text("data") mocker.patch.object(kibana.demisto, "getFilePath", return_value={"path": str(file_path), "name": "evidence.txt"}) http_mock = mocker.patch.object(kibana, "http_request", return_value={}) result = kibana.kibana_add_file_to_case({"case_id": "c1", "file_id": "f1"}, proxies=None) assert result == "Successfully added file evidence.txt to case c1" _, kwargs = http_mock.call_args assert kwargs["url_suffix"] == "/api/cases/c1/files"