mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Security Solution][API testing] Move and restructures Rule execution logic (#170765)
## Summary Following the initial work in this https://github.com/elastic/kibana/pull/166755 - Addresses part of https://github.com/elastic/kibana/issues/151902 for rule execution logic - Moved the utility files associated with rule execution logic to the new directory `security_solution_api_integration`. Files not actively used in the previous folder were moved, while duplicate files remained in their original positions. - Updated the CodeOwner file for the newly moved tests - Old/new group details, decisions, and execution time are mentioned in this[document](https://docs.google.com/document/d/1CRFfDWMzw3ob03euWIvT4-IoiLXjoiPWI8mTBqP4Zks/edit) - Added new `Alert` archive for version `8.8.0` - Resolved the issue with the `query.ts` test where the execution logic is executed last, encompassing the "query" test because it was unloading the alerts document and led to failures in subsequent tests. - For `Alert As Data` in **Serverless** the alert ancestor will be a data-stream however in **ESS** will be `.internal.alerts-security.alerts-default-000001'` | Action | File | New Path if moved | |--------|------|----------| | Delete| security_and_spaces/rule_execution_logic| - | | Delete|security_and_spaces/group5 | - | | Move|detection_engine_api_integration/security_and_spaces/group5|detections_response/default_license/rule_execution_logic/keyword_family| | Move|detection_engine_api_integration/security_and_spaces/rule_execution_logic| detections_response/default_license/rule_execution_logic/execution_logic | | Move |detection_engine_api_integration/security_and_spaces/group1/ignore_fields| detections_response/default_license/rule_execution_logic/ignore_fields.ts| | Move|detection_engine_api_integration/security_and_spaces/group1/runtime| detections_response/default_license/rule_execution_logic/runtime.ts | | Move|detection_engine_api_integration/security_and_spaces/group1/timestamps| detections_response/default_license/rule_execution_logic/timestamps.ts| --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
c713b91e66
commit
2b136a2d77
57 changed files with 10019 additions and 2186 deletions
|
@ -225,9 +225,7 @@ enabled:
|
|||
- x-pack/test/detection_engine_api_integration/basic/config.ts
|
||||
- x-pack/test/detection_engine_api_integration/security_and_spaces/group1/config.ts
|
||||
- x-pack/test/detection_engine_api_integration/security_and_spaces/group4/config.ts
|
||||
- x-pack/test/detection_engine_api_integration/security_and_spaces/group5/config.ts
|
||||
- x-pack/test/detection_engine_api_integration/security_and_spaces/group10/config.ts
|
||||
- x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/config.ts
|
||||
- x-pack/test/disable_ems/config.ts
|
||||
- x-pack/test/encrypted_saved_objects_api_integration/config.ts
|
||||
- x-pack/test/examples/config.ts
|
||||
|
@ -474,3 +472,5 @@ enabled:
|
|||
- x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/prebuilt_rules/large_prebuilt_rules_package/configs/ess.config.ts
|
||||
- x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/prebuilt_rules/update_prebuilt_rules_package/configs/serverless.config.ts
|
||||
- x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/prebuilt_rules/update_prebuilt_rules_package/configs/ess.config.ts
|
||||
- x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_execution_logic/configs/serverless.config.ts
|
||||
- x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_execution_logic/configs/ess.config.ts
|
|
@ -26,9 +26,6 @@ export default ({ loadTestFile }: FtrProviderContext): void => {
|
|||
loadTestFile(require.resolve('./perform_bulk_action_dry_run'));
|
||||
loadTestFile(require.resolve('./patch_rules'));
|
||||
loadTestFile(require.resolve('./read_privileges'));
|
||||
loadTestFile(require.resolve('./timestamps'));
|
||||
loadTestFile(require.resolve('./runtime'));
|
||||
loadTestFile(require.resolve('./throttle'));
|
||||
loadTestFile(require.resolve('./ignore_fields'));
|
||||
});
|
||||
};
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { FtrConfigProviderContext } from '@kbn/test';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default async function ({ readConfigFile }: FtrConfigProviderContext) {
|
||||
const functionalConfig = await readConfigFile(require.resolve('../config.base.ts'));
|
||||
|
||||
return {
|
||||
...functionalConfig.getAll(),
|
||||
testFiles: [require.resolve('.')],
|
||||
};
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { FtrProviderContext } from '../../common/ftr_provider_context';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default ({ loadTestFile }: FtrProviderContext): void => {
|
||||
describe('detection engine api security and spaces enabled - Group 5', function () {
|
||||
loadTestFile(require.resolve('./keyword_family'));
|
||||
});
|
||||
};
|
File diff suppressed because it is too large
Load diff
|
@ -1,28 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { DetectionAlert } from '@kbn/security-solution-plugin/common/api/detection_engine';
|
||||
import { ALERT_LAST_DETECTED, ALERT_START } from '@kbn/rule-data-utils';
|
||||
|
||||
export const removeRandomValuedProperties = (alert: DetectionAlert | undefined) => {
|
||||
if (!alert) {
|
||||
return undefined;
|
||||
}
|
||||
const {
|
||||
'kibana.version': version,
|
||||
'kibana.alert.rule.execution.uuid': execUuid,
|
||||
'kibana.alert.rule.uuid': uuid,
|
||||
'@timestamp': timestamp,
|
||||
'kibana.alert.rule.created_at': createdAt,
|
||||
'kibana.alert.rule.updated_at': updatedAt,
|
||||
'kibana.alert.uuid': alertUuid,
|
||||
[ALERT_START]: alertStart,
|
||||
[ALERT_LAST_DETECTED]: lastDetected,
|
||||
...restOfAlert
|
||||
} = alert;
|
||||
return restOfAlert;
|
||||
};
|
|
@ -39,7 +39,6 @@
|
|||
},
|
||||
"settings": {
|
||||
"index": {
|
||||
"refresh_interval": "1s",
|
||||
"number_of_replicas": "1",
|
||||
"number_of_shards": "1"
|
||||
}
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
},
|
||||
"settings": {
|
||||
"index": {
|
||||
"refresh_interval": "1s",
|
||||
"number_of_replicas": "1",
|
||||
"number_of_shards": "1"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,422 @@
|
|||
{
|
||||
"type": "doc",
|
||||
"value": {
|
||||
"id": "eabbdefc23da981f2b74ab58b82622a97bb9878caa11bc914e2adfacc94780f1",
|
||||
"index": ".alerts-security.alerts-default",
|
||||
"source": {
|
||||
"@timestamp": "2023-04-27T11:03:57.906Z",
|
||||
"Endpoint": {
|
||||
"capabilities": [
|
||||
"isolation",
|
||||
"kill_process",
|
||||
"suspend_process",
|
||||
"running_processes",
|
||||
"get_file",
|
||||
"execute"
|
||||
],
|
||||
"configuration": {
|
||||
"isolation": true
|
||||
},
|
||||
"policy": {
|
||||
"applied": {
|
||||
"endpoint_policy_version": 3,
|
||||
"id": "C2A9093E-E289-4C0A-AA44-8C32A414FA7A",
|
||||
"name": "With Eventing",
|
||||
"status": "success",
|
||||
"version": 5
|
||||
}
|
||||
},
|
||||
"state": {
|
||||
"isolation": true
|
||||
},
|
||||
"status": "enrolled"
|
||||
},
|
||||
"agent": {
|
||||
"id": "b563ce99-e373-4a1f-a5fe-97e956140aeb",
|
||||
"type": "endpoint",
|
||||
"version": "8.8.0"
|
||||
},
|
||||
"data_stream": {
|
||||
"dataset": "endpoint.alerts",
|
||||
"namespace": "default",
|
||||
"type": "logs"
|
||||
},
|
||||
"dll": [
|
||||
{
|
||||
"Ext": {
|
||||
"compile_time": 1534424710,
|
||||
"malware_classification": {
|
||||
"identifier": "Whitelisted",
|
||||
"score": 0,
|
||||
"threshold": 0,
|
||||
"version": "3.0.0"
|
||||
},
|
||||
"mapped_address": 5362483200,
|
||||
"mapped_size": 0
|
||||
},
|
||||
"code_signature": {
|
||||
"subject_name": "Cybereason Inc",
|
||||
"trusted": true
|
||||
},
|
||||
"hash": {
|
||||
"md5": "1f2d082566b0fc5f2c238a5180db7451",
|
||||
"sha1": "ca85243c0af6a6471bdaa560685c51eefd6dbc0d",
|
||||
"sha256": "8ad40c90a611d36eb8f9eb24fa04f7dbca713db383ff55a03aa0f382e92061a2"
|
||||
},
|
||||
"path": "C:\\Program Files\\Cybereason ActiveProbe\\AmSvc.exe",
|
||||
"pe": {
|
||||
"architecture": "x64"
|
||||
}
|
||||
}
|
||||
],
|
||||
"ecs": {
|
||||
"version": "1.4.0"
|
||||
},
|
||||
"elastic": {
|
||||
"agent": {
|
||||
"id": "b563ce99-e373-4a1f-a5fe-97e956140aeb"
|
||||
}
|
||||
},
|
||||
"event.action": "creation",
|
||||
"event.agent_id_status": "auth_metadata_missing",
|
||||
"event.category": "malware",
|
||||
"event.code": "malicious_file",
|
||||
"event.dataset": "endpoint",
|
||||
"event.id": "b28993d4-8b8a-4f0f-9f54-84a89bad66ae",
|
||||
"event.ingested": "2023-04-27T10:58:03Z",
|
||||
"event.kind": "signal",
|
||||
"event.module": "endpoint",
|
||||
"event.sequence": 5826,
|
||||
"event.type": "creation",
|
||||
"file": {
|
||||
"Ext": {
|
||||
"code_signature": [
|
||||
{
|
||||
"subject_name": "bad signer",
|
||||
"trusted": false
|
||||
}
|
||||
],
|
||||
"malware_classification": {
|
||||
"identifier": "endpointpe",
|
||||
"score": 1,
|
||||
"threshold": 0.66,
|
||||
"version": "3.0.33"
|
||||
},
|
||||
"quarantine_message": "fake quarantine message",
|
||||
"quarantine_result": true,
|
||||
"temp_file_path": "C:/temp/fake_malware.exe"
|
||||
},
|
||||
"accessed": 1682752652103,
|
||||
"created": 1682752652103,
|
||||
"hash": {
|
||||
"md5": "fake file md5",
|
||||
"sha1": "fake file sha1",
|
||||
"sha256": "fake file sha256"
|
||||
},
|
||||
"mtime": 1682752652103,
|
||||
"name": "fake_malware.exe",
|
||||
"owner": "SYSTEM",
|
||||
"path": "C:/fake_malware.exe",
|
||||
"size": 3456
|
||||
},
|
||||
"user": {
|
||||
"name": "user1"
|
||||
},
|
||||
"host": {
|
||||
"architecture": "wtnozeqvub",
|
||||
"hostname": "Host-fwarau82er",
|
||||
"id": "4260adf9-5e63-445d-92c6-e03359bcd342",
|
||||
"ip": [
|
||||
"10.249.37.72",
|
||||
"10.150.39.243",
|
||||
"10.186.17.170"
|
||||
],
|
||||
"mac": [
|
||||
"f5-f-97-dc-20-67",
|
||||
"b5-56-ca-98-81-ca",
|
||||
"22-86-39-4c-87-33"
|
||||
],
|
||||
"name": "Host-fwarau82er",
|
||||
"os": {
|
||||
"Ext": {
|
||||
"variant": "Darwin"
|
||||
},
|
||||
"family": "Darwin",
|
||||
"full": "macOS Monterey",
|
||||
"name": "macOS",
|
||||
"platform": "macOS",
|
||||
"version": "12.6.1"
|
||||
}
|
||||
},
|
||||
"kibana.alert.ancestors": [
|
||||
{
|
||||
"depth": 0,
|
||||
"id": "vT9cwocBh3b8EMpD8lsi",
|
||||
"index": ".ds-logs-endpoint.alerts-default-2023.04.27-000001",
|
||||
"type": "event"
|
||||
}
|
||||
],
|
||||
"kibana.alert.depth": 1,
|
||||
"kibana.alert.last_detected": "2023-04-27T11:03:57.993Z",
|
||||
"kibana.alert.original_event.action": "creation",
|
||||
"kibana.alert.original_event.agent_id_status": "auth_metadata_missing",
|
||||
"kibana.alert.original_event.category": "malware",
|
||||
"kibana.alert.original_event.code": "malicious_file",
|
||||
"kibana.alert.original_event.dataset": "endpoint",
|
||||
"kibana.alert.original_event.id": "b28993d4-8b8a-4f0f-9f54-84a89bad66ae",
|
||||
"kibana.alert.original_event.ingested": "2023-04-27T10:58:03Z",
|
||||
"kibana.alert.original_event.kind": "alert",
|
||||
"kibana.alert.original_event.module": "endpoint",
|
||||
"kibana.alert.original_event.sequence": 5826,
|
||||
"kibana.alert.original_event.type": "creation",
|
||||
"kibana.alert.original_time": "2023-04-29T07:17:32.103Z",
|
||||
"kibana.alert.reason": "malware event with process malware writer, file fake_malware.exe, on Host-fwarau82er created medium alert Endpoint Security.",
|
||||
"kibana.alert.risk_score": 47,
|
||||
"kibana.alert.rule.actions": [
|
||||
],
|
||||
"kibana.alert.rule.author": [
|
||||
"Elastic"
|
||||
],
|
||||
"kibana.alert.rule.category": "Custom Query Rule",
|
||||
"kibana.alert.rule.consumer": "siem",
|
||||
"kibana.alert.rule.created_at": "2023-04-27T10:58:27.546Z",
|
||||
"kibana.alert.rule.created_by": "elastic",
|
||||
"kibana.alert.rule.description": "Generates a detection alert each time an Elastic Endpoint Security alert is received. Enabling this rule allows you to immediately begin investigating your Endpoint alerts.",
|
||||
"kibana.alert.rule.enabled": true,
|
||||
"kibana.alert.rule.exceptions_list": [
|
||||
{
|
||||
"id": "endpoint_list",
|
||||
"list_id": "endpoint_list",
|
||||
"namespace_type": "agnostic",
|
||||
"type": "endpoint"
|
||||
}
|
||||
],
|
||||
"kibana.alert.rule.execution.uuid": "ebf843ff-e0e1-47f8-9ed2-cc8066afbcef",
|
||||
"kibana.alert.rule.false_positives": [
|
||||
],
|
||||
"kibana.alert.rule.from": "now-10m",
|
||||
"kibana.alert.rule.immutable": true,
|
||||
"kibana.alert.rule.indices": [
|
||||
"logs-endpoint.alerts-*"
|
||||
],
|
||||
"kibana.alert.rule.interval": "5m",
|
||||
"kibana.alert.rule.license": "Elastic License v2",
|
||||
"kibana.alert.rule.max_signals": 10000,
|
||||
"kibana.alert.rule.name": "Endpoint Security",
|
||||
"kibana.alert.rule.parameters": {
|
||||
"author": [
|
||||
"Elastic"
|
||||
],
|
||||
"description": "Generates a detection alert each time an Elastic Endpoint Security alert is received. Enabling this rule allows you to immediately begin investigating your Endpoint alerts.",
|
||||
"exceptions_list": [
|
||||
{
|
||||
"id": "endpoint_list",
|
||||
"list_id": "endpoint_list",
|
||||
"namespace_type": "agnostic",
|
||||
"type": "endpoint"
|
||||
}
|
||||
],
|
||||
"false_positives": [
|
||||
],
|
||||
"from": "now-10m",
|
||||
"immutable": true,
|
||||
"index": [
|
||||
"logs-endpoint.alerts-*"
|
||||
],
|
||||
"language": "kuery",
|
||||
"license": "Elastic License v2",
|
||||
"max_signals": 10000,
|
||||
"query": "event.kind:alert and event.module:(endpoint and not endgame)\n",
|
||||
"references": [
|
||||
],
|
||||
"related_integrations": [
|
||||
{
|
||||
"package": "endpoint",
|
||||
"version": "^8.2.0"
|
||||
}
|
||||
],
|
||||
"required_fields": [
|
||||
{
|
||||
"ecs": true,
|
||||
"name": "event.kind",
|
||||
"type": "keyword"
|
||||
},
|
||||
{
|
||||
"ecs": true,
|
||||
"name": "event.module",
|
||||
"type": "keyword"
|
||||
}
|
||||
],
|
||||
"risk_score": 47,
|
||||
"risk_score_mapping": [
|
||||
{
|
||||
"field": "event.risk_score",
|
||||
"operator": "equals",
|
||||
"value": ""
|
||||
}
|
||||
],
|
||||
"rule_id": "9a1a2dae-0b5f-4c3d-8305-a268d404c306",
|
||||
"rule_name_override": "message",
|
||||
"setup": "",
|
||||
"severity": "medium",
|
||||
"severity_mapping": [
|
||||
{
|
||||
"field": "event.severity",
|
||||
"operator": "equals",
|
||||
"severity": "low",
|
||||
"value": "21"
|
||||
},
|
||||
{
|
||||
"field": "event.severity",
|
||||
"operator": "equals",
|
||||
"severity": "medium",
|
||||
"value": "47"
|
||||
},
|
||||
{
|
||||
"field": "event.severity",
|
||||
"operator": "equals",
|
||||
"severity": "high",
|
||||
"value": "73"
|
||||
},
|
||||
{
|
||||
"field": "event.severity",
|
||||
"operator": "equals",
|
||||
"severity": "critical",
|
||||
"value": "99"
|
||||
}
|
||||
],
|
||||
"threat": [
|
||||
],
|
||||
"timestamp_override": "event.ingested",
|
||||
"to": "now",
|
||||
"type": "query",
|
||||
"version": 101
|
||||
},
|
||||
"kibana.alert.rule.producer": "siem",
|
||||
"kibana.alert.rule.references": [
|
||||
],
|
||||
"kibana.alert.rule.revision": 0,
|
||||
"kibana.alert.rule.risk_score": 47,
|
||||
"kibana.alert.rule.risk_score_mapping": [
|
||||
{
|
||||
"field": "event.risk_score",
|
||||
"operator": "equals",
|
||||
"value": ""
|
||||
}
|
||||
],
|
||||
"kibana.alert.rule.rule_id": "9a1a2dae-0b5f-4c3d-8305-a268d404c306",
|
||||
"kibana.alert.rule.rule_name_override": "message",
|
||||
"kibana.alert.rule.rule_type_id": "siem.queryRule",
|
||||
"kibana.alert.rule.severity": "medium",
|
||||
"kibana.alert.rule.severity_mapping": [
|
||||
{
|
||||
"field": "event.severity",
|
||||
"operator": "equals",
|
||||
"severity": "low",
|
||||
"value": "21"
|
||||
},
|
||||
{
|
||||
"field": "event.severity",
|
||||
"operator": "equals",
|
||||
"severity": "medium",
|
||||
"value": "47"
|
||||
},
|
||||
{
|
||||
"field": "event.severity",
|
||||
"operator": "equals",
|
||||
"severity": "high",
|
||||
"value": "73"
|
||||
},
|
||||
{
|
||||
"field": "event.severity",
|
||||
"operator": "equals",
|
||||
"severity": "critical",
|
||||
"value": "99"
|
||||
}
|
||||
],
|
||||
"kibana.alert.rule.tags": [
|
||||
"Elastic",
|
||||
"Endpoint Security"
|
||||
],
|
||||
"kibana.alert.rule.threat": [
|
||||
],
|
||||
"kibana.alert.rule.timestamp_override": "event.ingested",
|
||||
"kibana.alert.rule.to": "now",
|
||||
"kibana.alert.rule.type": "query",
|
||||
"kibana.alert.rule.updated_at": "2023-04-27T10:58:27.546Z",
|
||||
"kibana.alert.rule.updated_by": "elastic",
|
||||
"kibana.alert.rule.uuid": "7015a3e2-e4ea-11ed-8c11-49608884878f",
|
||||
"kibana.alert.rule.version": 101,
|
||||
"kibana.alert.severity": "medium",
|
||||
"kibana.alert.start": "2023-04-27T11:03:57.993Z",
|
||||
"kibana.alert.status": "active",
|
||||
"kibana.alert.url": "http://localhost:5601/app/security/alerts/redirect/eabbdefc23da981f2b74ab58b82622a97bb9878caa11bc914e2adfacc94780f1?index=.alerts-security.alerts-default×tamp=2023-04-27T11:03:57.906Z",
|
||||
"kibana.alert.uuid": "eabbdefc23da981f2b74ab58b82622a97bb9878caa11bc914e2adfacc94780f1",
|
||||
"kibana.alert.workflow_status": "open",
|
||||
"kibana.space_ids": [
|
||||
"default"
|
||||
],
|
||||
"kibana.version": "8.8.0",
|
||||
"process": {
|
||||
"Ext": {
|
||||
"ancestry": [
|
||||
"qa5jgw1wr7",
|
||||
"5k1hclygc6"
|
||||
],
|
||||
"code_signature": [
|
||||
{
|
||||
"subject_name": "bad signer",
|
||||
"trusted": false
|
||||
}
|
||||
],
|
||||
"token": {
|
||||
"domain": "NT AUTHORITY",
|
||||
"integrity_level": 16384,
|
||||
"integrity_level_name": "system",
|
||||
"privileges": [
|
||||
{
|
||||
"description": "Replace a process level token",
|
||||
"enabled": false,
|
||||
"name": "SeAssignPrimaryTokenPrivilege"
|
||||
}
|
||||
],
|
||||
"sid": "S-1-5-18",
|
||||
"type": "tokenPrimary",
|
||||
"user": "SYSTEM"
|
||||
},
|
||||
"user": "SYSTEM"
|
||||
},
|
||||
"entity_id": "nqh8ts6ves",
|
||||
"entry_leader": {
|
||||
"entity_id": "jnm38bel0w",
|
||||
"name": "fake entry",
|
||||
"pid": 791
|
||||
},
|
||||
"executable": "C:/malware.exe",
|
||||
"group_leader": {
|
||||
"entity_id": "jnm38bel0w",
|
||||
"name": "fake leader",
|
||||
"pid": 848
|
||||
},
|
||||
"hash": {
|
||||
"md5": "fake md5",
|
||||
"sha1": "fake sha1",
|
||||
"sha256": "fake sha256"
|
||||
},
|
||||
"name": "malware writer",
|
||||
"parent": {
|
||||
"entity_id": "qa5jgw1wr7",
|
||||
"pid": 1
|
||||
},
|
||||
"pid": 2,
|
||||
"session_leader": {
|
||||
"entity_id": "jnm38bel0w",
|
||||
"name": "fake session",
|
||||
"pid": 909
|
||||
},
|
||||
"start": 1682752652103,
|
||||
"uptime": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -32,7 +32,6 @@
|
|||
},
|
||||
"settings": {
|
||||
"index": {
|
||||
"refresh_interval": "1s",
|
||||
"number_of_replicas": "1",
|
||||
"number_of_shards": "1"
|
||||
}
|
||||
|
|
|
@ -7027,7 +7027,6 @@
|
|||
},
|
||||
"settings": {
|
||||
"index": {
|
||||
"refresh_interval": "1s",
|
||||
"number_of_replicas": "1",
|
||||
"number_of_shards": "1",
|
||||
"mapping": {
|
||||
|
|
|
@ -108,7 +108,6 @@
|
|||
},
|
||||
"settings": {
|
||||
"index": {
|
||||
"refresh_interval": "1s",
|
||||
"number_of_replicas": "1",
|
||||
"number_of_shards": "1"
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
},
|
||||
"settings": {
|
||||
"index": {
|
||||
"refresh_interval": "1s",
|
||||
"number_of_replicas": "1",
|
||||
"number_of_shards": "1"
|
||||
}
|
||||
|
|
|
@ -30,7 +30,6 @@
|
|||
},
|
||||
"settings": {
|
||||
"index": {
|
||||
"refresh_interval": "1s",
|
||||
"number_of_replicas": "1",
|
||||
"number_of_shards": "1"
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
},
|
||||
"settings": {
|
||||
"index": {
|
||||
"refresh_interval": "1s",
|
||||
"number_of_replicas": "1",
|
||||
"number_of_shards": "1"
|
||||
}
|
||||
|
|
|
@ -11,15 +11,18 @@ export interface CreateTestConfigOptions {
|
|||
kbnTestServerArgs?: string[];
|
||||
kbnTestServerEnv?: Record<string, string>;
|
||||
}
|
||||
import { services } from '../../../../test_serverless/api_integration/services';
|
||||
|
||||
export function createTestConfig(options: CreateTestConfigOptions) {
|
||||
return async ({ readConfigFile }: FtrConfigProviderContext) => {
|
||||
const svlSharedConfig = await readConfigFile(
|
||||
require.resolve('../../../../test_serverless/shared/config.base.ts')
|
||||
);
|
||||
|
||||
return {
|
||||
...svlSharedConfig.getAll(),
|
||||
services: {
|
||||
...services,
|
||||
},
|
||||
kbnTestServer: {
|
||||
...svlSharedConfig.get('kbnTestServer'),
|
||||
serverArgs: [
|
||||
|
|
|
@ -0,0 +1,418 @@
|
|||
{
|
||||
"type": "doc",
|
||||
"value": {
|
||||
"id": "978783",
|
||||
"index": "filebeat-8.0.0-2021.01.26-000001",
|
||||
"source": {
|
||||
"@timestamp": "2021-01-26T11:09:05.529Z",
|
||||
"agent": {
|
||||
"ephemeral_id": "b7b56c3e-1f27-4c69-96f4-aa9ca47888d0",
|
||||
"id": "69acb5f0-1e79-4cfe-a4dc-e0dbf229ff51",
|
||||
"name": "MacBook-Pro-de-Gloria.local",
|
||||
"type": "filebeat",
|
||||
"version": "8.0.0"
|
||||
},
|
||||
"ecs": {
|
||||
"version": "1.6.0"
|
||||
},
|
||||
"event": {
|
||||
"category": "threat",
|
||||
"created": "2021-01-26T11:09:05.529Z",
|
||||
"dataset": "ti_abusech.malware",
|
||||
"ingested": "2021-01-26T11:09:06.595350Z",
|
||||
"kind": "enrichment",
|
||||
"module": "threatintel",
|
||||
"reference": "https://urlhaus.abuse.ch/url/978783/",
|
||||
"type": "indicator"
|
||||
},
|
||||
"fileset": {
|
||||
"name": "abuseurl"
|
||||
},
|
||||
"input": {
|
||||
"type": "httpjson"
|
||||
},
|
||||
"service": {
|
||||
"type": "threatintel"
|
||||
},
|
||||
"tags": [
|
||||
"threatintel-abuseurls",
|
||||
"forwarded"
|
||||
],
|
||||
"threat": {
|
||||
"indicator": {
|
||||
"description": "domain should match the auditbeat hosts' data's source.ip",
|
||||
"domain": "159.89.119.67",
|
||||
"first_seen": "2021-01-26T11:09:04.000Z",
|
||||
"provider": "geenensp",
|
||||
"type": "url",
|
||||
"url": {
|
||||
"full": "http://159.89.119.67:59600/bin.sh",
|
||||
"scheme": "http"
|
||||
}
|
||||
}
|
||||
},
|
||||
"threatintel": {
|
||||
"abuseurl": {
|
||||
"blacklists": {
|
||||
"spamhaus_dbl": "not listed",
|
||||
"surbl": "not listed"
|
||||
},
|
||||
"larted": false,
|
||||
"tags": null,
|
||||
"threat": "malware_download",
|
||||
"url_status": "online"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
"type": "doc",
|
||||
"value": {
|
||||
"id": "978784",
|
||||
"index": "filebeat-8.0.0-2021.01.26-000001",
|
||||
"source": {
|
||||
"@timestamp": "2021-01-26T11:09:05.529Z",
|
||||
"agent": {
|
||||
"ephemeral_id": "b7b56c3e-1f27-4c69-96f4-aa9ca47888d0",
|
||||
"id": "69acb5f0-1e79-4cfe-a4dc-e0dbf229ff51",
|
||||
"name": "MacBook-Pro-de-Gloria.local",
|
||||
"type": "filebeat",
|
||||
"version": "8.0.0"
|
||||
},
|
||||
"ecs": {
|
||||
"version": "1.6.0"
|
||||
},
|
||||
"event": {
|
||||
"category": "threat",
|
||||
"created": "2021-01-26T11:09:05.529Z",
|
||||
"dataset": "ti_abusech.malware",
|
||||
"ingested": "2021-01-26T11:09:06.616763Z",
|
||||
"kind": "enrichment",
|
||||
"module": "threatintel",
|
||||
"reference": "https://urlhaus.abuse.ch/url/978782/",
|
||||
"type": "indicator"
|
||||
},
|
||||
"fileset": {
|
||||
"name": "abuseurl"
|
||||
},
|
||||
"input": {
|
||||
"type": "httpjson"
|
||||
},
|
||||
"service": {
|
||||
"type": "threatintel"
|
||||
},
|
||||
"tags": [
|
||||
"threatintel-abuseurls",
|
||||
"forwarded"
|
||||
],
|
||||
"threat": {
|
||||
"indicator": {
|
||||
"description": "this should not match the auditbeat hosts data",
|
||||
"ip": "125.46.136.106",
|
||||
"first_seen": "2021-01-26T11:06:03.000Z",
|
||||
"provider": "geenensp",
|
||||
"type": "ip"
|
||||
}
|
||||
},
|
||||
"threatintel": {
|
||||
"abuseurl": {
|
||||
"blacklists": {
|
||||
"spamhaus_dbl": "not listed",
|
||||
"surbl": "not listed"
|
||||
},
|
||||
"larted": true,
|
||||
"tags": [
|
||||
"32-bit",
|
||||
"elf",
|
||||
"mips"
|
||||
],
|
||||
"threat": "malware_download",
|
||||
"url_status": "online"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
"type": "doc",
|
||||
"value": {
|
||||
"id": "978785",
|
||||
"index": "filebeat-8.0.0-2021.01.26-000001",
|
||||
"source": {
|
||||
"@timestamp": "2021-01-26T11:09:05.529Z",
|
||||
"agent": {
|
||||
"ephemeral_id": "b7b56c3e-1f27-4c69-96f4-aa9ca47888d0",
|
||||
"id": "69acb5f0-1e79-4cfe-a4dc-e0dbf229ff51",
|
||||
"name": "MacBook-Pro-de-Gloria.local",
|
||||
"type": "filebeat",
|
||||
"version": "8.0.0"
|
||||
},
|
||||
"ecs": {
|
||||
"version": "1.6.0"
|
||||
},
|
||||
"event": {
|
||||
"category": "threat",
|
||||
"created": "2021-01-26T11:09:05.529Z",
|
||||
"dataset": "ti_abusech.malware",
|
||||
"ingested": "2021-01-26T11:09:06.616763Z",
|
||||
"kind": "enrichment",
|
||||
"module": "threatintel",
|
||||
"reference": "https://urlhaus.abuse.ch/url/978782/",
|
||||
"type": "indicator"
|
||||
},
|
||||
"fileset": {
|
||||
"name": "abuseurl"
|
||||
},
|
||||
"input": {
|
||||
"type": "httpjson"
|
||||
},
|
||||
"service": {
|
||||
"type": "threatintel"
|
||||
},
|
||||
"tags": [
|
||||
"threatintel-abuseurls",
|
||||
"forwarded"
|
||||
],
|
||||
"threat": {
|
||||
"indicator": {
|
||||
"description": "this should match auditbeat/hosts on both port and ip",
|
||||
"ip": "45.115.45.3",
|
||||
"port": 57324,
|
||||
"first_seen": "2021-01-26T11:06:03.000Z",
|
||||
"provider": "geenensp",
|
||||
"type": "url"
|
||||
}
|
||||
},
|
||||
"threatintel": {
|
||||
"abuseurl": {
|
||||
"blacklists": {
|
||||
"spamhaus_dbl": "not listed",
|
||||
"surbl": "not listed"
|
||||
},
|
||||
"larted": true,
|
||||
"tags": [
|
||||
"32-bit",
|
||||
"elf",
|
||||
"mips"
|
||||
],
|
||||
"threat": "malware_download",
|
||||
"url_status": "online"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
"type": "doc",
|
||||
"value": {
|
||||
"id": "978787",
|
||||
"index": "filebeat-8.0.0-2021.01.26-000001",
|
||||
"source": {
|
||||
"@timestamp": "2021-01-26T11:09:05.529Z",
|
||||
"agent": {
|
||||
"ephemeral_id": "b7b56c3e-1f27-4c69-96f4-aa9ca47888d0",
|
||||
"id": "69acb5f0-1e79-4cfe-a4dc-e0dbf229ff51",
|
||||
"name": "MacBook-Pro-de-Gloria.local",
|
||||
"type": "filebeat",
|
||||
"version": "8.0.0"
|
||||
},
|
||||
"ecs": {
|
||||
"version": "1.6.0"
|
||||
},
|
||||
"event": {
|
||||
"category": "threat",
|
||||
"created": "2021-01-26T11:09:05.529Z",
|
||||
"dataset": "ti_abusech.malware",
|
||||
"ingested": "2021-01-26T11:09:06.616763Z",
|
||||
"kind": "enrichment",
|
||||
"module": "threatintel",
|
||||
"reference": "https://urlhaus.abuse.ch/url/978782/",
|
||||
"type": "indicator"
|
||||
},
|
||||
"fileset": {
|
||||
"name": "abuseurl"
|
||||
},
|
||||
"input": {
|
||||
"type": "httpjson"
|
||||
},
|
||||
"service": {
|
||||
"type": "threatintel"
|
||||
},
|
||||
"tags": [
|
||||
"threatintel-abuseurls",
|
||||
"forwarded"
|
||||
],
|
||||
"threat": {
|
||||
"indicator": {
|
||||
"description": "this should match auditbeat/hosts on ip",
|
||||
"ip": "45.115.45.3",
|
||||
"first_seen": "2021-01-26T11:06:03.000Z",
|
||||
"provider": "other_provider",
|
||||
"type": "ip"
|
||||
}
|
||||
},
|
||||
"threatintel": {
|
||||
"abuseurl": {
|
||||
"blacklists": {
|
||||
"spamhaus_dbl": "not listed",
|
||||
"surbl": "not listed"
|
||||
},
|
||||
"larted": true,
|
||||
"tags": [
|
||||
"32-bit",
|
||||
"elf",
|
||||
"mips"
|
||||
],
|
||||
"threat": "malware_download",
|
||||
"url_status": "online"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
"type": "doc",
|
||||
"value": {
|
||||
"id": "978766",
|
||||
"index": "filebeat-8.0.0-2021.01.26-000001",
|
||||
"source": {
|
||||
"@timestamp": "2021-01-26T11:09:05.529Z",
|
||||
"agent": {
|
||||
"ephemeral_id": "b7b56c3e-1f27-4c69-96f4-aa9ca47888d0",
|
||||
"id": "69acb5f0-1e79-4cfe-a4dc-e0dbf229ff51",
|
||||
"name": "MacBook-Pro-de-Gloria.local",
|
||||
"type": "filebeat",
|
||||
"version": "8.0.0"
|
||||
},
|
||||
"ecs": {
|
||||
"version": "1.6.0"
|
||||
},
|
||||
"event": {
|
||||
"category": "threat",
|
||||
"created": "2021-01-26T11:09:05.529Z",
|
||||
"dataset": "ti_abusech.malware",
|
||||
"ingested": "2021-01-26T11:09:06.595350Z",
|
||||
"kind": "enrichment",
|
||||
"module": "threatintel",
|
||||
"reference": "https://urlhaus.abuse.ch/url/978783/",
|
||||
"type": "indicator"
|
||||
},
|
||||
"fileset": {
|
||||
"name": "abuseurl"
|
||||
},
|
||||
"input": {
|
||||
"type": "httpjson"
|
||||
},
|
||||
"service": {
|
||||
"type": "threatintel"
|
||||
},
|
||||
"tags": [
|
||||
"threatintel-abuseurls",
|
||||
"forwarded"
|
||||
],
|
||||
"threat": {
|
||||
"indicator": {
|
||||
"description": "domain should match the auditbeat hosts' data's source.ip",
|
||||
"domain": "172.16.0.0",
|
||||
"ip": "8.8.8.8",
|
||||
"port": 777,
|
||||
"first_seen": "2021-01-26T11:09:04.000Z",
|
||||
"provider": "geenensp",
|
||||
"type": "url",
|
||||
"url": {
|
||||
"full": "http://159.89.119.67:59600/bin.sh",
|
||||
"scheme": "http"
|
||||
}
|
||||
}
|
||||
},
|
||||
"threatintel": {
|
||||
"abuseurl": {
|
||||
"blacklists": {
|
||||
"spamhaus_dbl": "not listed",
|
||||
"surbl": "not listed"
|
||||
},
|
||||
"larted": false,
|
||||
"tags": null,
|
||||
"threat": "malware_download",
|
||||
"url_status": "online"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
"type": "doc",
|
||||
"value": {
|
||||
"id": "978767",
|
||||
"index": "filebeat-8.0.0-2021.01.26-000001",
|
||||
"source": {
|
||||
"@timestamp": "2021-01-26T11:09:05.529Z",
|
||||
"agent": {
|
||||
"ephemeral_id": "b7b56c3e-1f27-4c69-96f4-aa9ca47888d0",
|
||||
"id": "69acb5f0-1e79-4cfe-a4dc-e0dbf229ff51",
|
||||
"name": "MacBook-Pro-de-Gloria.local",
|
||||
"type": "filebeat",
|
||||
"version": "8.0.0"
|
||||
},
|
||||
"ecs": {
|
||||
"version": "1.6.0"
|
||||
},
|
||||
"event": {
|
||||
"category": "threat",
|
||||
"created": "2021-01-26T11:09:05.529Z",
|
||||
"dataset": "ti_abusech.malware",
|
||||
"ingested": "2021-01-26T11:09:06.595350Z",
|
||||
"kind": "enrichment",
|
||||
"module": "threatintel",
|
||||
"reference": "https://urlhaus.abuse.ch/url/978783/",
|
||||
"type": "indicator"
|
||||
},
|
||||
"fileset": {
|
||||
"name": "abuseurl"
|
||||
},
|
||||
"input": {
|
||||
"type": "httpjson"
|
||||
},
|
||||
"service": {
|
||||
"type": "threatintel"
|
||||
},
|
||||
"tags": [
|
||||
"threatintel-abuseurls",
|
||||
"forwarded"
|
||||
],
|
||||
"threat": {
|
||||
"indicator": {
|
||||
"description": "domain should match the auditbeat hosts' data's source.ip",
|
||||
"domain": "172.16.0.0",
|
||||
"ip": "9.9.9.9",
|
||||
"port": 123,
|
||||
"first_seen": "2021-01-26T11:09:04.000Z",
|
||||
"provider": "geenensp",
|
||||
"type": "url",
|
||||
"url": {
|
||||
"full": "http://159.89.119.67:59600/bin.sh",
|
||||
"scheme": "http"
|
||||
}
|
||||
}
|
||||
},
|
||||
"threatintel": {
|
||||
"abuseurl": {
|
||||
"blacklists": {
|
||||
"spamhaus_dbl": "not listed",
|
||||
"surbl": "not listed"
|
||||
},
|
||||
"larted": false,
|
||||
"tags": null,
|
||||
"threat": "malware_download",
|
||||
"url_status": "online"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,240 @@
|
|||
{
|
||||
"type": "index",
|
||||
"value": {
|
||||
"aliases": {},
|
||||
"index": "filebeat-8.0.0-2021.01.26-000001",
|
||||
"mappings": {
|
||||
"_meta": {
|
||||
"beat": "filebeat",
|
||||
"version": "7.0.0"
|
||||
},
|
||||
"properties": {
|
||||
"@timestamp": {
|
||||
"type": "date"
|
||||
},
|
||||
"@version": {
|
||||
"ignore_above": 1024,
|
||||
"type": "keyword"
|
||||
},
|
||||
"threat": {
|
||||
"properties": {
|
||||
"framework": {
|
||||
"ignore_above": 1024,
|
||||
"type": "keyword"
|
||||
},
|
||||
"indicator": {
|
||||
"properties": {
|
||||
"as": {
|
||||
"properties": {
|
||||
"number": {
|
||||
"type": "long"
|
||||
},
|
||||
"organization": {
|
||||
"properties": {
|
||||
"name": {
|
||||
"fields": {
|
||||
"text": {
|
||||
"norms": false,
|
||||
"type": "text"
|
||||
}
|
||||
},
|
||||
"ignore_above": 1024,
|
||||
"type": "keyword"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"confidence": {
|
||||
"ignore_above": 1024,
|
||||
"type": "keyword"
|
||||
},
|
||||
"dataset": {
|
||||
"ignore_above": 1024,
|
||||
"type": "keyword"
|
||||
},
|
||||
"description": {
|
||||
"type": "wildcard"
|
||||
},
|
||||
"domain": {
|
||||
"ignore_above": 1024,
|
||||
"type": "keyword"
|
||||
},
|
||||
"email": {
|
||||
"properties": {
|
||||
"address": {
|
||||
"ignore_above": 1024,
|
||||
"type": "keyword"
|
||||
}
|
||||
}
|
||||
},
|
||||
"first_seen": {
|
||||
"type": "date"
|
||||
},
|
||||
"geo": {
|
||||
"properties": {
|
||||
"city_name": {
|
||||
"ignore_above": 1024,
|
||||
"type": "keyword"
|
||||
},
|
||||
"continent_name": {
|
||||
"ignore_above": 1024,
|
||||
"type": "keyword"
|
||||
},
|
||||
"country_iso_code": {
|
||||
"ignore_above": 1024,
|
||||
"type": "keyword"
|
||||
},
|
||||
"country_name": {
|
||||
"ignore_above": 1024,
|
||||
"type": "keyword"
|
||||
},
|
||||
"location": {
|
||||
"type": "geo_point"
|
||||
},
|
||||
"name": {
|
||||
"ignore_above": 1024,
|
||||
"type": "keyword"
|
||||
},
|
||||
"region_iso_code": {
|
||||
"ignore_above": 1024,
|
||||
"type": "keyword"
|
||||
},
|
||||
"region_name": {
|
||||
"ignore_above": 1024,
|
||||
"type": "keyword"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ip": {
|
||||
"type": "ip"
|
||||
},
|
||||
"last_seen": {
|
||||
"type": "date"
|
||||
},
|
||||
"marking": {
|
||||
"properties": {
|
||||
"tlp": {
|
||||
"ignore_above": 1024,
|
||||
"type": "keyword"
|
||||
}
|
||||
}
|
||||
},
|
||||
"matched": {
|
||||
"properties": {
|
||||
"atomic": {
|
||||
"ignore_above": 1024,
|
||||
"type": "keyword"
|
||||
},
|
||||
"field": {
|
||||
"ignore_above": 1024,
|
||||
"type": "keyword"
|
||||
},
|
||||
"type": {
|
||||
"ignore_above": 1024,
|
||||
"type": "keyword"
|
||||
}
|
||||
}
|
||||
},
|
||||
"module": {
|
||||
"ignore_above": 1024,
|
||||
"type": "keyword"
|
||||
},
|
||||
"port": {
|
||||
"type": "long"
|
||||
},
|
||||
"provider": {
|
||||
"ignore_above": 1024,
|
||||
"type": "keyword"
|
||||
},
|
||||
"scanner_stats": {
|
||||
"type": "long"
|
||||
},
|
||||
"sightings": {
|
||||
"type": "long"
|
||||
},
|
||||
"type": {
|
||||
"ignore_above": 1024,
|
||||
"type": "keyword"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tactic": {
|
||||
"properties": {
|
||||
"id": {
|
||||
"ignore_above": 1024,
|
||||
"type": "keyword"
|
||||
},
|
||||
"name": {
|
||||
"ignore_above": 1024,
|
||||
"type": "keyword"
|
||||
},
|
||||
"reference": {
|
||||
"ignore_above": 1024,
|
||||
"type": "keyword"
|
||||
}
|
||||
}
|
||||
},
|
||||
"technique": {
|
||||
"properties": {
|
||||
"id": {
|
||||
"ignore_above": 1024,
|
||||
"type": "keyword"
|
||||
},
|
||||
"name": {
|
||||
"fields": {
|
||||
"text": {
|
||||
"norms": false,
|
||||
"type": "text"
|
||||
}
|
||||
},
|
||||
"ignore_above": 1024,
|
||||
"type": "keyword"
|
||||
},
|
||||
"reference": {
|
||||
"ignore_above": 1024,
|
||||
"type": "keyword"
|
||||
},
|
||||
"subtechnique": {
|
||||
"properties": {
|
||||
"id": {
|
||||
"ignore_above": 1024,
|
||||
"type": "keyword"
|
||||
},
|
||||
"name": {
|
||||
"fields": {
|
||||
"text": {
|
||||
"norms": false,
|
||||
"type": "text"
|
||||
}
|
||||
},
|
||||
"ignore_above": 1024,
|
||||
"type": "keyword"
|
||||
},
|
||||
"reference": {
|
||||
"ignore_above": 1024,
|
||||
"type": "keyword"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"index": {
|
||||
"auto_expand_replicas": "0-1",
|
||||
"mapping": {
|
||||
"total_fields": {
|
||||
"limit": "10000"
|
||||
}
|
||||
},
|
||||
"number_of_replicas": "0",
|
||||
"number_of_shards": "1",
|
||||
"refresh_interval": "5s"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -83,6 +83,12 @@
|
|||
"prebuilt_rules_update_prebuilt_rules_package:runner:serverless": "npm run run-tests:dr:default prebuilt_rules/update_prebuilt_rules_package serverless serverlessEnv",
|
||||
"prebuilt_rules_update_prebuilt_rules_package:qa:serverless": "npm run run-tests:dr:default prebuilt_rules/update_prebuilt_rules_package serverless qaEnv",
|
||||
"prebuilt_rules_update_prebuilt_rules_package:server:ess": "npm run initialize-server:dr:default prebuilt_rules/update_prebuilt_rules_package ess",
|
||||
"prebuilt_rules_update_prebuilt_rules_package:runner:ess": "npm run run-tests:dr:default prebuilt_rules/update_prebuilt_rules_package ess essEnvs"
|
||||
"prebuilt_rules_update_prebuilt_rules_package:runner:ess": "npm run run-tests:dr:default prebuilt_rules/update_prebuilt_rules_package ess essEnvs",
|
||||
"rule_execution_logic:server:serverless": "npm run initialize-server:dr:default rule_execution_logic serverless",
|
||||
"rule_execution_logic:runner:serverless": "npm run run-tests:dr:default rule_execution_logic serverless serverlessEnv",
|
||||
"rule_execution_logic:qa:serverless": "npm run run-tests:dr:default rule_execution_logic serverless qaEnv",
|
||||
"rule_execution_logic:server:ess": "npm run initialize-server:dr:default rule_execution_logic ess",
|
||||
"rule_execution_logic:runner:ess": "npm run run-tests:dr:default rule_execution_logic ess essEnv"
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ import {
|
|||
waitFor,
|
||||
waitForRuleSuccess,
|
||||
waitForAlertsToBePresent,
|
||||
removeRandomValuedProperties,
|
||||
removeRandomValuedPropertiesFromAlert,
|
||||
} from '../../utils';
|
||||
import { FtrProviderContext } from '../../../../ftr_provider_context';
|
||||
|
||||
|
@ -231,11 +231,11 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
expect(signalsOpen.hits.hits.length).greaterThan(0);
|
||||
const hit = signalsOpen.hits.hits[0];
|
||||
expect(hit._source?.kibana).to.eql(undefined);
|
||||
const source = removeRandomValuedProperties(hit._source);
|
||||
const source = removeRandomValuedPropertiesFromAlert(hit._source);
|
||||
expect(source).to.eql({
|
||||
'kibana.alert.rule.category': 'Custom Query Rule',
|
||||
'kibana.alert.rule.consumer': 'siem',
|
||||
'kibana.alert.rule.name': 'Signal Testing Query',
|
||||
'kibana.alert.rule.name': 'Alert Testing Query',
|
||||
'kibana.alert.rule.producer': 'siem',
|
||||
'kibana.alert.rule.rule_type_id': 'siem.queryRule',
|
||||
'kibana.space_ids': ['default'],
|
||||
|
@ -322,7 +322,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
'kibana.alert.workflow_tags': [],
|
||||
'kibana.alert.depth': 2,
|
||||
'kibana.alert.reason':
|
||||
'event on security-linux-1 created high alert Signal Testing Query.',
|
||||
'event on security-linux-1 created high alert Alert Testing Query.',
|
||||
'kibana.alert.severity': 'high',
|
||||
'kibana.alert.risk_score': 1,
|
||||
'kibana.alert.rule.parameters': {
|
||||
|
@ -393,11 +393,11 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
expect(signalsOpen.hits.hits.length).greaterThan(0);
|
||||
const hit = signalsOpen.hits.hits[0];
|
||||
expect(hit._source?.kibana).to.eql(undefined);
|
||||
const source = removeRandomValuedProperties(hit._source);
|
||||
const source = removeRandomValuedPropertiesFromAlert(hit._source);
|
||||
expect(source).to.eql({
|
||||
'kibana.alert.rule.category': 'Custom Query Rule',
|
||||
'kibana.alert.rule.consumer': 'siem',
|
||||
'kibana.alert.rule.name': 'Signal Testing Query',
|
||||
'kibana.alert.rule.name': 'Alert Testing Query',
|
||||
'kibana.alert.rule.producer': 'siem',
|
||||
'kibana.alert.rule.rule_type_id': 'siem.queryRule',
|
||||
'kibana.space_ids': ['default'],
|
||||
|
@ -484,7 +484,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
'kibana.alert.workflow_tags': [],
|
||||
'kibana.alert.depth': 2,
|
||||
'kibana.alert.reason':
|
||||
'event on security-linux-1 created high alert Signal Testing Query.',
|
||||
'event on security-linux-1 created high alert Alert Testing Query.',
|
||||
'kibana.alert.severity': 'high',
|
||||
'kibana.alert.risk_score': 1,
|
||||
'kibana.alert.rule.parameters': {
|
||||
|
|
|
@ -7,12 +7,16 @@
|
|||
|
||||
import { FtrConfigProviderContext } from '@kbn/test';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default async function ({ readConfigFile }: FtrConfigProviderContext) {
|
||||
const functionalConfig = await readConfigFile(require.resolve('../config.base.ts'));
|
||||
const functionalConfig = await readConfigFile(
|
||||
require.resolve('../../../../../config/ess/config.base.trial')
|
||||
);
|
||||
|
||||
return {
|
||||
...functionalConfig.getAll(),
|
||||
testFiles: [require.resolve('.')],
|
||||
testFiles: [require.resolve('..')],
|
||||
junit: {
|
||||
reportName: 'Detection Engine API Integration Tests - ESS - Rule Execution Logic',
|
||||
},
|
||||
};
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { createTestConfig } from '../../../../../config/serverless/config.base';
|
||||
|
||||
export default createTestConfig({
|
||||
testFiles: [require.resolve('..')],
|
||||
junit: {
|
||||
reportName: 'Detection Engine API Integration Tests - Serverless - Rule Execution Logic',
|
||||
},
|
||||
kbnTestServerArgs: [
|
||||
`--xpack.securitySolution.alertIgnoreFields=${JSON.stringify([
|
||||
'testing_ignored.constant',
|
||||
'/testing_regex*/',
|
||||
])}`, // See tests within the file "ignore_fields.ts" which use these values in "alertIgnoreFields"
|
||||
],
|
||||
});
|
|
@ -27,35 +27,40 @@ import {
|
|||
ALERT_ORIGINAL_EVENT_CATEGORY,
|
||||
ALERT_GROUP_ID,
|
||||
} from '@kbn/security-solution-plugin/common/field_maps/field_names';
|
||||
import { getMaxSignalsWarning } from '@kbn/security-solution-plugin/server/lib/detection_engine/rule_types/utils/utils';
|
||||
import { getMaxSignalsWarning as getMaxAlertsWarning } from '@kbn/security-solution-plugin/server/lib/detection_engine/rule_types/utils/utils';
|
||||
import {
|
||||
createRule,
|
||||
deleteAllRules,
|
||||
deleteAllAlerts,
|
||||
getEqlRuleForSignalTesting,
|
||||
getOpenSignals,
|
||||
getEqlRuleForAlertTesting,
|
||||
getOpenAlerts,
|
||||
getPreviewAlerts,
|
||||
previewRule,
|
||||
} from '../../utils';
|
||||
import { FtrProviderContext } from '../../common/ftr_provider_context';
|
||||
} from '../../../utils';
|
||||
import { FtrProviderContext } from '../../../../../ftr_provider_context';
|
||||
import { EsArchivePathBuilder } from '../../../../../es_archive_path_builder';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default ({ getService }: FtrProviderContext) => {
|
||||
const supertest = getService('supertest');
|
||||
const esArchiver = getService('esArchiver');
|
||||
const es = getService('es');
|
||||
const log = getService('log');
|
||||
// TODO: add a new service
|
||||
const config = getService('config');
|
||||
const isServerless = config.get('serverless');
|
||||
const dataPathBuilder = new EsArchivePathBuilder(isServerless);
|
||||
const auditPath = dataPathBuilder.getPath('auditbeat/hosts');
|
||||
|
||||
describe('EQL type rules', () => {
|
||||
describe('@ess @serverless EQL type rules', () => {
|
||||
before(async () => {
|
||||
await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/hosts');
|
||||
await esArchiver.load(auditPath);
|
||||
await esArchiver.load(
|
||||
'x-pack/test/functional/es_archives/security_solution/timestamp_override_6'
|
||||
);
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/hosts');
|
||||
await esArchiver.unload(auditPath);
|
||||
await esArchiver.unload(
|
||||
'x-pack/test/functional/es_archives/security_solution/timestamp_override_6'
|
||||
);
|
||||
|
@ -64,21 +69,21 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
});
|
||||
|
||||
// First test creates a real rule - remaining tests use preview API
|
||||
it('generates a correctly formatted signal from EQL non-sequence queries', async () => {
|
||||
it('generates a correctly formatted alert from EQL non-sequence queries', async () => {
|
||||
const rule: EqlRuleCreateProps = {
|
||||
...getEqlRuleForSignalTesting(['auditbeat-*']),
|
||||
...getEqlRuleForAlertTesting(['auditbeat-*']),
|
||||
query: 'configuration where agent.id=="a1d7b39c-f898-4dbe-a761-efb61939302d"',
|
||||
};
|
||||
const createdRule = await createRule(supertest, log, rule);
|
||||
const alerts = await getOpenSignals(supertest, log, es, createdRule);
|
||||
const alerts = await getOpenAlerts(supertest, log, es, createdRule);
|
||||
expect(alerts.hits.hits.length).eql(1);
|
||||
const fullSignal = alerts.hits.hits[0]._source;
|
||||
if (!fullSignal) {
|
||||
return expect(fullSignal).to.be.ok();
|
||||
const fullAlert = alerts.hits.hits[0]._source;
|
||||
if (!fullAlert) {
|
||||
return expect(fullAlert).to.be.ok();
|
||||
}
|
||||
|
||||
expect(fullSignal).eql({
|
||||
...fullSignal,
|
||||
expect(fullAlert).eql({
|
||||
...fullAlert,
|
||||
agent: {
|
||||
ephemeral_id: '0010d67a-14f7-41da-be30-489fea735967',
|
||||
hostname: 'suricata-zeek-sensor-toronto',
|
||||
|
@ -145,9 +150,9 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
},
|
||||
},
|
||||
[ALERT_REASON]:
|
||||
'configuration event on suricata-zeek-sensor-toronto created high alert Signal Testing Query.',
|
||||
[ALERT_RULE_UUID]: fullSignal[ALERT_RULE_UUID],
|
||||
[ALERT_ORIGINAL_TIME]: fullSignal[ALERT_ORIGINAL_TIME],
|
||||
'configuration event on suricata-zeek-sensor-toronto created high alert Alert Testing Query.',
|
||||
[ALERT_RULE_UUID]: fullAlert[ALERT_RULE_UUID],
|
||||
[ALERT_ORIGINAL_TIME]: fullAlert[ALERT_ORIGINAL_TIME],
|
||||
[ALERT_WORKFLOW_STATUS]: 'open',
|
||||
[ALERT_WORKFLOW_TAGS]: [],
|
||||
[ALERT_DEPTH]: 1,
|
||||
|
@ -167,41 +172,41 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
});
|
||||
});
|
||||
|
||||
it('generates up to max_signals for non-sequence EQL queries', async () => {
|
||||
const maxSignals = 200;
|
||||
it('generates up to max_alerts for non-sequence EQL queries', async () => {
|
||||
const maxAlerts = 200;
|
||||
const rule: EqlRuleCreateProps = {
|
||||
...getEqlRuleForSignalTesting(['auditbeat-*']),
|
||||
max_signals: maxSignals,
|
||||
...getEqlRuleForAlertTesting(['auditbeat-*']),
|
||||
max_signals: maxAlerts,
|
||||
};
|
||||
const { previewId } = await previewRule({ supertest, rule });
|
||||
const previewAlerts = await getPreviewAlerts({ es, previewId, size: maxSignals * 2 });
|
||||
expect(previewAlerts.length).eql(maxSignals);
|
||||
const previewAlerts = await getPreviewAlerts({ es, previewId, size: maxAlerts * 2 });
|
||||
expect(previewAlerts.length).eql(maxAlerts);
|
||||
});
|
||||
|
||||
it('generates max signals warning when circuit breaker is hit', async () => {
|
||||
it('generates max alerts warning when circuit breaker is hit', async () => {
|
||||
const rule: EqlRuleCreateProps = {
|
||||
...getEqlRuleForSignalTesting(['auditbeat-*']),
|
||||
...getEqlRuleForAlertTesting(['auditbeat-*']),
|
||||
};
|
||||
const { logs } = await previewRule({ supertest, rule });
|
||||
expect(logs[0].warnings).contain(getMaxSignalsWarning());
|
||||
expect(logs[0].warnings).contain(getMaxAlertsWarning());
|
||||
});
|
||||
|
||||
it('uses the provided event_category_override', async () => {
|
||||
const rule: EqlRuleCreateProps = {
|
||||
...getEqlRuleForSignalTesting(['auditbeat-*']),
|
||||
...getEqlRuleForAlertTesting(['auditbeat-*']),
|
||||
query: 'config_change where agent.id=="a1d7b39c-f898-4dbe-a761-efb61939302d"',
|
||||
event_category_override: 'auditd.message_type',
|
||||
};
|
||||
const { previewId } = await previewRule({ supertest, rule });
|
||||
const previewAlerts = await getPreviewAlerts({ es, previewId });
|
||||
expect(previewAlerts.length).eql(1);
|
||||
const fullSignal = previewAlerts[0]._source;
|
||||
if (!fullSignal) {
|
||||
return expect(fullSignal).to.be.ok();
|
||||
const fullAlert = previewAlerts[0]._source;
|
||||
if (!fullAlert) {
|
||||
return expect(fullAlert).to.be.ok();
|
||||
}
|
||||
|
||||
expect(fullSignal).eql({
|
||||
...fullSignal,
|
||||
expect(fullAlert).eql({
|
||||
...fullAlert,
|
||||
auditd: {
|
||||
data: {
|
||||
audit_enabled: '1',
|
||||
|
@ -236,9 +241,9 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
},
|
||||
},
|
||||
[ALERT_REASON]:
|
||||
'configuration event on suricata-zeek-sensor-toronto created high alert Signal Testing Query.',
|
||||
[ALERT_RULE_UUID]: fullSignal[ALERT_RULE_UUID],
|
||||
[ALERT_ORIGINAL_TIME]: fullSignal[ALERT_ORIGINAL_TIME],
|
||||
'configuration event on suricata-zeek-sensor-toronto created high alert Alert Testing Query.',
|
||||
[ALERT_RULE_UUID]: fullAlert[ALERT_RULE_UUID],
|
||||
[ALERT_ORIGINAL_TIME]: fullAlert[ALERT_ORIGINAL_TIME],
|
||||
[ALERT_WORKFLOW_STATUS]: 'open',
|
||||
[ALERT_DEPTH]: 1,
|
||||
[ALERT_ANCESTORS]: [
|
||||
|
@ -259,7 +264,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
|
||||
it('uses the provided timestamp_field', async () => {
|
||||
const rule: EqlRuleCreateProps = {
|
||||
...getEqlRuleForSignalTesting(['fake.index.1']),
|
||||
...getEqlRuleForAlertTesting(['fake.index.1']),
|
||||
query: 'any where true',
|
||||
timestamp_field: 'created_at',
|
||||
};
|
||||
|
@ -273,7 +278,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
|
||||
it('uses the provided tiebreaker_field', async () => {
|
||||
const rule: EqlRuleCreateProps = {
|
||||
...getEqlRuleForSignalTesting(['fake.index.1']),
|
||||
...getEqlRuleForAlertTesting(['fake.index.1']),
|
||||
query: 'any where true',
|
||||
tiebreaker_field: 'locale',
|
||||
};
|
||||
|
@ -285,9 +290,9 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
expect(createdAtHits).to.eql(['es', 'pt', 'ua']);
|
||||
});
|
||||
|
||||
it('generates building block signals from EQL sequences in the expected form', async () => {
|
||||
it('generates building block alerts from EQL sequences in the expected form', async () => {
|
||||
const rule: EqlRuleCreateProps = {
|
||||
...getEqlRuleForSignalTesting(['auditbeat-*']),
|
||||
...getEqlRuleForAlertTesting(['auditbeat-*']),
|
||||
query: 'sequence by host.name [anomoly where true] [any where true]', // TODO: spelling
|
||||
};
|
||||
const { previewId } = await previewRule({ supertest, rule });
|
||||
|
@ -298,13 +303,13 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
get(alert._source, ALERT_ORIGINAL_EVENT_CATEGORY) === 'anomoly'
|
||||
);
|
||||
expect(buildingBlock).not.eql(undefined);
|
||||
const fullSignal = buildingBlock?._source;
|
||||
if (!fullSignal) {
|
||||
return expect(fullSignal).to.be.ok();
|
||||
const fullAlert = buildingBlock?._source;
|
||||
if (!fullAlert) {
|
||||
return expect(fullAlert).to.be.ok();
|
||||
}
|
||||
|
||||
expect(fullSignal).eql({
|
||||
...fullSignal,
|
||||
expect(fullAlert).eql({
|
||||
...fullAlert,
|
||||
agent: {
|
||||
ephemeral_id: '1b4978a0-48be-49b1-ac96-323425b389ab',
|
||||
hostname: 'zeek-sensor-amsterdam',
|
||||
|
@ -409,10 +414,10 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
},
|
||||
},
|
||||
[ALERT_REASON]:
|
||||
'anomoly event with process bro, by root on zeek-sensor-amsterdam created high alert Signal Testing Query.',
|
||||
[ALERT_RULE_UUID]: fullSignal[ALERT_RULE_UUID],
|
||||
[ALERT_GROUP_ID]: fullSignal[ALERT_GROUP_ID],
|
||||
[ALERT_ORIGINAL_TIME]: fullSignal[ALERT_ORIGINAL_TIME],
|
||||
'anomoly event with process bro, by root on zeek-sensor-amsterdam created high alert Alert Testing Query.',
|
||||
[ALERT_RULE_UUID]: fullAlert[ALERT_RULE_UUID],
|
||||
[ALERT_GROUP_ID]: fullAlert[ALERT_GROUP_ID],
|
||||
[ALERT_ORIGINAL_TIME]: fullAlert[ALERT_ORIGINAL_TIME],
|
||||
[ALERT_WORKFLOW_STATUS]: 'open',
|
||||
[ALERT_DEPTH]: 1,
|
||||
[ALERT_ANCESTORS]: [
|
||||
|
@ -431,9 +436,9 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
});
|
||||
});
|
||||
|
||||
it('generates shell signals from EQL sequences in the expected form', async () => {
|
||||
it('generates shell alerts from EQL sequences in the expected form', async () => {
|
||||
const rule: EqlRuleCreateProps = {
|
||||
...getEqlRuleForSignalTesting(['auditbeat-*']),
|
||||
...getEqlRuleForAlertTesting(['auditbeat-*']),
|
||||
query: 'sequence by host.name [anomoly where true] [any where true]',
|
||||
};
|
||||
const { previewId } = await previewRule({ supertest, rule });
|
||||
|
@ -480,7 +485,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
[ALERT_DEPTH]: 2,
|
||||
[ALERT_GROUP_ID]: source[ALERT_GROUP_ID],
|
||||
[ALERT_REASON]:
|
||||
'event by root on zeek-sensor-amsterdam created high alert Signal Testing Query.',
|
||||
'event by root on zeek-sensor-amsterdam created high alert Alert Testing Query.',
|
||||
[ALERT_RULE_UUID]: source[ALERT_RULE_UUID],
|
||||
[ALERT_ANCESTORS]: [
|
||||
{
|
||||
|
@ -513,28 +518,28 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
});
|
||||
});
|
||||
|
||||
it('generates up to max_signals with an EQL rule', async () => {
|
||||
const maxSignals = 200;
|
||||
it('generates up to max_alerts with an EQL rule', async () => {
|
||||
const maxAlerts = 200;
|
||||
const rule: EqlRuleCreateProps = {
|
||||
...getEqlRuleForSignalTesting(['auditbeat-*']),
|
||||
...getEqlRuleForAlertTesting(['auditbeat-*']),
|
||||
query: 'sequence by host.name [any where true] [any where true]',
|
||||
max_signals: maxSignals,
|
||||
max_signals: maxAlerts,
|
||||
};
|
||||
const { previewId } = await previewRule({ supertest, rule });
|
||||
const previewAlerts = await getPreviewAlerts({ es, previewId, size: maxSignals * 5 });
|
||||
// For EQL rules, max_signals is the maximum number of detected sequences: each sequence has a building block
|
||||
// alert for each event in the sequence, so max_signals=200 results in 400 building blocks in addition to
|
||||
const previewAlerts = await getPreviewAlerts({ es, previewId, size: maxAlerts * 5 });
|
||||
// For EQL rules, max_alerts is the maximum number of detected sequences: each sequence has a building block
|
||||
// alert for each event in the sequence, so max_alerts=200 results in 400 building blocks in addition to
|
||||
// 200 regular alerts
|
||||
expect(previewAlerts.length).eql(maxSignals * 3);
|
||||
const shellSignals = previewAlerts.filter((alert) => alert._source?.[ALERT_DEPTH] === 2);
|
||||
expect(previewAlerts.length).eql(maxAlerts * 3);
|
||||
const shellAlerts = previewAlerts.filter((alert) => alert._source?.[ALERT_DEPTH] === 2);
|
||||
const buildingBlocks = previewAlerts.filter((alert) => alert._source?.[ALERT_DEPTH] === 1);
|
||||
expect(shellSignals.length).eql(maxSignals);
|
||||
expect(buildingBlocks.length).eql(maxSignals * 2);
|
||||
expect(shellAlerts.length).eql(maxAlerts);
|
||||
expect(buildingBlocks.length).eql(maxAlerts * 2);
|
||||
});
|
||||
|
||||
it('generates signals when an index name contains special characters to encode', async () => {
|
||||
it('generates alerts when an index name contains special characters to encode', async () => {
|
||||
const rule: EqlRuleCreateProps = {
|
||||
...getEqlRuleForSignalTesting(['auditbeat-*', '<my-index-{now/d}*>']),
|
||||
...getEqlRuleForAlertTesting(['auditbeat-*', '<my-index-{now/d}*>']),
|
||||
query: 'configuration where agent.id=="a1d7b39c-f898-4dbe-a761-efb61939302d"',
|
||||
};
|
||||
const { previewId } = await previewRule({ supertest, rule });
|
||||
|
@ -544,7 +549,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
|
||||
it('uses the provided filters', async () => {
|
||||
const rule: EqlRuleCreateProps = {
|
||||
...getEqlRuleForSignalTesting(['auditbeat-*']),
|
||||
...getEqlRuleForAlertTesting(['auditbeat-*']),
|
||||
query: 'any where true',
|
||||
filters: [
|
||||
{
|
||||
|
@ -599,18 +604,18 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
|
||||
it('should be enriched with host risk score', async () => {
|
||||
const rule: EqlRuleCreateProps = {
|
||||
...getEqlRuleForSignalTesting(['auditbeat-*']),
|
||||
...getEqlRuleForAlertTesting(['auditbeat-*']),
|
||||
query: 'configuration where agent.id=="a1d7b39c-f898-4dbe-a761-efb61939302d"',
|
||||
};
|
||||
const { previewId } = await previewRule({ supertest, rule });
|
||||
const previewAlerts = await getPreviewAlerts({ es, previewId });
|
||||
expect(previewAlerts.length).eql(1);
|
||||
const fullSignal = previewAlerts[0]._source;
|
||||
if (!fullSignal) {
|
||||
return expect(fullSignal).to.be.ok();
|
||||
const fullAlert = previewAlerts[0]._source;
|
||||
if (!fullAlert) {
|
||||
return expect(fullAlert).to.be.ok();
|
||||
}
|
||||
expect(fullSignal?.host?.risk?.calculated_level).to.eql('Critical');
|
||||
expect(fullSignal?.host?.risk?.calculated_score_norm).to.eql(96);
|
||||
expect(fullAlert?.host?.risk?.calculated_level).to.eql('Critical');
|
||||
expect(fullAlert?.host?.risk?.calculated_score_norm).to.eql(96);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -13,23 +13,22 @@ import { EsqlRuleCreateProps } from '@kbn/security-solution-plugin/common/api/de
|
|||
import { getCreateEsqlRulesSchemaMock } from '@kbn/security-solution-plugin/common/api/detection_engine/model/rule_schema/mocks';
|
||||
import { RuleExecutionStatusEnum } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_monitoring';
|
||||
|
||||
import { getMaxSignalsWarning } from '@kbn/security-solution-plugin/server/lib/detection_engine/rule_types/utils/utils';
|
||||
import { getMaxSignalsWarning as getMaxAlertsWarning } from '@kbn/security-solution-plugin/server/lib/detection_engine/rule_types/utils/utils';
|
||||
import {
|
||||
deleteAllRules,
|
||||
deleteAllAlerts,
|
||||
getPreviewAlerts,
|
||||
previewRule,
|
||||
createRule,
|
||||
getOpenSignals as getOpenAlerts,
|
||||
} from '../../utils';
|
||||
import { FtrProviderContext } from '../../common/ftr_provider_context';
|
||||
import { previewRuleWithExceptionEntries } from '../../utils/preview_rule_with_exception_entries';
|
||||
import { deleteAllExceptions } from '../../../lists_api_integration/utils';
|
||||
import { dataGeneratorFactory } from '../../utils/data_generator';
|
||||
import { removeRandomValuedProperties } from './utils';
|
||||
import { patchRule } from '../../utils/patch_rule';
|
||||
getOpenAlerts,
|
||||
dataGeneratorFactory,
|
||||
previewRuleWithExceptionEntries,
|
||||
removeRandomValuedPropertiesFromAlert,
|
||||
patchRule,
|
||||
} from '../../../utils';
|
||||
import { deleteAllExceptions } from '../../../../../../lists_api_integration/utils';
|
||||
import { FtrProviderContext } from '../../../../../ftr_provider_context';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default ({ getService }: FtrProviderContext) => {
|
||||
const supertest = getService('supertest');
|
||||
const esArchiver = getService('esArchiver');
|
||||
|
@ -47,7 +46,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
*/
|
||||
const internalIdPipe = (id: string) => `| where id=="${id}"`;
|
||||
|
||||
describe('ES|QL rule type', () => {
|
||||
describe('@ess ES|QL rule type', () => {
|
||||
before(async () => {
|
||||
await esArchiver.load('x-pack/test/functional/es_archives/security_solution/ecs_compliant');
|
||||
});
|
||||
|
@ -84,7 +83,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
const alerts = await getOpenAlerts(supertest, log, es, createdRule);
|
||||
|
||||
expect(alerts.hits.hits.length).toBe(1);
|
||||
expect(removeRandomValuedProperties(alerts.hits.hits[0]._source)).toEqual({
|
||||
expect(removeRandomValuedPropertiesFromAlert(alerts.hits.hits[0]._source)).toEqual({
|
||||
'kibana.alert.rule.parameters': {
|
||||
description: 'Detecting root and admin users',
|
||||
risk_score: 55,
|
||||
|
@ -602,8 +601,8 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('max signals', () => {
|
||||
it('generates max signals warning when circuit breaker is exceeded', async () => {
|
||||
describe('max alerts', () => {
|
||||
it('generates max alerts warning when circuit breaker is exceeded', async () => {
|
||||
const id = uuidv4();
|
||||
const rule: EsqlRuleCreateProps = {
|
||||
...getCreateEsqlRulesSchemaMock('rule-1', true),
|
||||
|
@ -629,7 +628,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
timeframeEnd: new Date('2020-10-28T06:30:00.000Z'),
|
||||
});
|
||||
|
||||
expect(logs[0].warnings).toEqual(expect.arrayContaining([getMaxSignalsWarning()]));
|
||||
expect(logs[0].warnings).toEqual(expect.arrayContaining([getMaxAlertsWarning()]));
|
||||
|
||||
const previewAlerts = await getPreviewAlerts({
|
||||
es,
|
||||
|
@ -640,7 +639,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
expect(previewAlerts.length).toBe(100);
|
||||
});
|
||||
|
||||
it("doesn't generate max signals warning when circuit breaker is met but not exceeded", async () => {
|
||||
it("doesn't generate max alerts warning when circuit breaker is met but not exceeded", async () => {
|
||||
const id = uuidv4();
|
||||
const rule: EsqlRuleCreateProps = {
|
||||
...getCreateEsqlRulesSchemaMock('rule-1', true),
|
||||
|
@ -665,7 +664,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
rule,
|
||||
timeframeEnd: new Date('2020-10-28T06:30:00.000Z'),
|
||||
});
|
||||
expect(logs[0].warnings).not.toEqual(expect.arrayContaining([getMaxSignalsWarning()]));
|
||||
expect(logs[0].warnings).not.toEqual(expect.arrayContaining([getMaxAlertsWarning()]));
|
||||
|
||||
const previewAlerts = await getPreviewAlerts({
|
||||
es,
|
||||
|
@ -676,7 +675,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
expect(previewAlerts.length).toBe(100);
|
||||
});
|
||||
|
||||
it('should work for max signals > 100', async () => {
|
||||
it('should work for max alerts > 100', async () => {
|
||||
const id = uuidv4();
|
||||
const rule: EsqlRuleCreateProps = {
|
||||
...getCreateEsqlRulesSchemaMock('rule-1', true),
|
|
@ -5,11 +5,10 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { FtrProviderContext } from '../../common/ftr_provider_context';
|
||||
import { FtrProviderContext } from '../../../../../ftr_provider_context';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default ({ loadTestFile }: FtrProviderContext): void => {
|
||||
describe('detection engine api security and spaces enabled - rule execution logic', function () {
|
||||
describe('Execution logic', function () {
|
||||
loadTestFile(require.resolve('./eql'));
|
||||
loadTestFile(require.resolve('./esql'));
|
||||
loadTestFile(require.resolve('./machine_learning'));
|
||||
|
@ -18,7 +17,6 @@ export default ({ loadTestFile }: FtrProviderContext): void => {
|
|||
loadTestFile(require.resolve('./threat_match'));
|
||||
loadTestFile(require.resolve('./threshold'));
|
||||
loadTestFile(require.resolve('./non_ecs_fields'));
|
||||
|
||||
loadTestFile(require.resolve('./query'));
|
||||
});
|
||||
};
|
|
@ -24,33 +24,38 @@ import {
|
|||
ALERT_DEPTH,
|
||||
ALERT_ORIGINAL_TIME,
|
||||
} from '@kbn/security-solution-plugin/common/field_maps/field_names';
|
||||
import { getMaxSignalsWarning } from '@kbn/security-solution-plugin/server/lib/detection_engine/rule_types/utils/utils';
|
||||
import { getMaxSignalsWarning as getMaxAlertsWarning } from '@kbn/security-solution-plugin/server/lib/detection_engine/rule_types/utils/utils';
|
||||
import { expect } from 'expect';
|
||||
import {
|
||||
createListsIndex,
|
||||
deleteAllExceptions,
|
||||
deleteListsIndex,
|
||||
importFile,
|
||||
} from '../../../lists_api_integration/utils';
|
||||
import { FtrProviderContext } from '../../common/ftr_provider_context';
|
||||
} from '../../../../../../lists_api_integration/utils';
|
||||
import {
|
||||
createRule,
|
||||
deleteAllRules,
|
||||
deleteAllAlerts,
|
||||
executeSetupModuleRequest,
|
||||
forceStartDatafeeds,
|
||||
getOpenSignals,
|
||||
getOpenAlerts,
|
||||
getPreviewAlerts,
|
||||
previewRule,
|
||||
previewRuleWithExceptionEntries,
|
||||
} from '../../utils';
|
||||
} from '../../../utils';
|
||||
import { FtrProviderContext } from '../../../../../ftr_provider_context';
|
||||
import { EsArchivePathBuilder } from '../../../../../es_archive_path_builder';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default ({ getService }: FtrProviderContext) => {
|
||||
const supertest = getService('supertest');
|
||||
const esArchiver = getService('esArchiver');
|
||||
const es = getService('es');
|
||||
const log = getService('log');
|
||||
// TODO: add a new service
|
||||
const config = getService('config');
|
||||
const isServerless = config.get('serverless');
|
||||
const dataPathBuilder = new EsArchivePathBuilder(isServerless);
|
||||
const auditPath = dataPathBuilder.getPath('auditbeat/hosts');
|
||||
|
||||
const siemModule = 'security_linux_v3';
|
||||
const mlJobId = 'v3_linux_anomalous_network_activity';
|
||||
|
@ -66,17 +71,17 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
rule_id: 'ml-rule-id',
|
||||
};
|
||||
|
||||
describe('Machine learning type rules', () => {
|
||||
describe('@ess @serverless Machine learning type rules', () => {
|
||||
before(async () => {
|
||||
// Order is critical here: auditbeat data must be loaded before attempting to start the ML job,
|
||||
// as the job looks for certain indices on start
|
||||
await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/hosts');
|
||||
await esArchiver.load(auditPath);
|
||||
await executeSetupModuleRequest({ module: siemModule, rspCode: 200, supertest });
|
||||
await forceStartDatafeeds({ jobId: mlJobId, rspCode: 200, supertest });
|
||||
await esArchiver.load('x-pack/test/functional/es_archives/security_solution/anomalies');
|
||||
});
|
||||
after(async () => {
|
||||
await esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/hosts');
|
||||
await esArchiver.unload(auditPath);
|
||||
await esArchiver.unload('x-pack/test/functional/es_archives/security_solution/anomalies');
|
||||
await deleteAllAlerts(supertest, log, es);
|
||||
await deleteAllRules(supertest, log);
|
||||
|
@ -85,11 +90,11 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
// First test creates a real rule - remaining tests use preview API
|
||||
it('should create 1 alert from ML rule when record meets anomaly_threshold', async () => {
|
||||
const createdRule = await createRule(supertest, log, rule);
|
||||
const alerts = await getOpenSignals(supertest, log, es, createdRule);
|
||||
const alerts = await getOpenAlerts(supertest, log, es, createdRule);
|
||||
expect(alerts.hits.hits.length).toBe(1);
|
||||
const signal = alerts.hits.hits[0];
|
||||
const alert = alerts.hits.hits[0];
|
||||
|
||||
expect(signal._source).toEqual(
|
||||
expect(alert._source).toEqual(
|
||||
expect.objectContaining({
|
||||
'@timestamp': expect.any(String),
|
||||
[ALERT_RULE_EXECUTION_UUID]: expect.any(String),
|
||||
|
@ -160,23 +165,23 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
);
|
||||
});
|
||||
|
||||
it('generates max signals warning when circuit breaker is exceeded', async () => {
|
||||
it('@skipInQA generates max alerts warning when circuit breaker is exceeded', async () => {
|
||||
const { logs } = await previewRule({
|
||||
supertest,
|
||||
rule: { ...rule, anomaly_threshold: 1, max_signals: 5 }, // This threshold generates 10 alerts with the current esArchive
|
||||
});
|
||||
expect(logs[0].warnings).toContain(getMaxSignalsWarning());
|
||||
expect(logs[0].warnings).toContain(getMaxAlertsWarning());
|
||||
});
|
||||
|
||||
it("doesn't generate max signals warning when circuit breaker is met, but not exceeded", async () => {
|
||||
it("doesn't generate max alerts warning when circuit breaker is met, but not exceeded", async () => {
|
||||
const { logs } = await previewRule({
|
||||
supertest,
|
||||
rule: { ...rule, anomaly_threshold: 1, max_signals: 10 },
|
||||
});
|
||||
expect(logs[0].warnings).not.toContain(getMaxSignalsWarning());
|
||||
expect(logs[0].warnings).not.toContain(getMaxAlertsWarning());
|
||||
});
|
||||
|
||||
it('should create 7 alerts from ML rule when records meet anomaly_threshold', async () => {
|
||||
it('@skipInQA should create 7 alerts from ML rule when records meet anomaly_threshold', async () => {
|
||||
const { previewId } = await previewRule({
|
||||
supertest,
|
||||
rule: { ...rule, anomaly_threshold: 20 },
|
||||
|
@ -189,7 +194,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
afterEach(async () => {
|
||||
await deleteAllExceptions(supertest, log);
|
||||
});
|
||||
it('generates no signals when an exception is added for an ML rule', async () => {
|
||||
it('generates no alerts when an exception is added for an ML rule', async () => {
|
||||
const { previewId } = await previewRuleWithExceptionEntries({
|
||||
supertest,
|
||||
log,
|
||||
|
@ -220,7 +225,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
await deleteAllExceptions(supertest, log);
|
||||
});
|
||||
|
||||
it('generates no signals when a value list exception is added for an ML rule', async () => {
|
||||
it('generates no alerts when a value list exception is added for an ML rule', async () => {
|
||||
const valueListId = 'value-list-id';
|
||||
await importFile(supertest, log, 'keyword', ['mothra'], valueListId);
|
||||
const { previewId } = await previewRuleWithExceptionEntries({
|
||||
|
@ -255,14 +260,14 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
await esArchiver.unload('x-pack/test/functional/es_archives/entity/risks');
|
||||
});
|
||||
|
||||
it('should be enriched with host risk score', async () => {
|
||||
it('@skipInQA should be enriched with host risk score', async () => {
|
||||
const { previewId } = await previewRule({ supertest, rule });
|
||||
const previewAlerts = await getPreviewAlerts({ es, previewId });
|
||||
expect(previewAlerts.length).toBe(1);
|
||||
const fullSignal = previewAlerts[0]._source;
|
||||
const fullAlert = previewAlerts[0]._source;
|
||||
|
||||
expect(fullSignal?.host?.risk?.calculated_level).toBe('Low');
|
||||
expect(fullSignal?.host?.risk?.calculated_score_norm).toBe(1);
|
||||
expect(fullAlert?.host?.risk?.calculated_level).toBe('Low');
|
||||
expect(fullAlert?.host?.risk?.calculated_score_norm).toBe(1);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -12,26 +12,25 @@ import { NewTermsRuleCreateProps } from '@kbn/security-solution-plugin/common/ap
|
|||
import { orderBy } from 'lodash';
|
||||
import { getCreateNewTermsRulesSchemaMock } from '@kbn/security-solution-plugin/common/api/detection_engine/model/rule_schema/mocks';
|
||||
|
||||
import { getMaxSignalsWarning } from '@kbn/security-solution-plugin/server/lib/detection_engine/rule_types/utils/utils';
|
||||
import { getMaxSignalsWarning as getMaxAlertsWarning } from '@kbn/security-solution-plugin/server/lib/detection_engine/rule_types/utils/utils';
|
||||
import {
|
||||
createRule,
|
||||
deleteAllRules,
|
||||
deleteAllAlerts,
|
||||
getOpenSignals,
|
||||
getOpenAlerts,
|
||||
getPreviewAlerts,
|
||||
previewRule,
|
||||
} from '../../utils';
|
||||
import { FtrProviderContext } from '../../common/ftr_provider_context';
|
||||
import { previewRuleWithExceptionEntries } from '../../utils/preview_rule_with_exception_entries';
|
||||
import { deleteAllExceptions } from '../../../lists_api_integration/utils';
|
||||
import { dataGeneratorFactory } from '../../utils/data_generator';
|
||||
|
||||
import { removeRandomValuedProperties } from './utils';
|
||||
dataGeneratorFactory,
|
||||
previewRuleWithExceptionEntries,
|
||||
removeRandomValuedPropertiesFromAlert,
|
||||
} from '../../../utils';
|
||||
import { deleteAllExceptions } from '../../../../../../lists_api_integration/utils';
|
||||
import { FtrProviderContext } from '../../../../../ftr_provider_context';
|
||||
import { EsArchivePathBuilder } from '../../../../../es_archive_path_builder';
|
||||
|
||||
const historicalWindowStart = '2022-10-13T05:00:04.000Z';
|
||||
const ruleExecutionStart = '2022-10-19T05:00:04.000Z';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default ({ getService }: FtrProviderContext) => {
|
||||
const supertest = getService('supertest');
|
||||
const esArchiver = getService('esArchiver');
|
||||
|
@ -42,7 +41,12 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
index: 'new_terms',
|
||||
log,
|
||||
});
|
||||
|
||||
// TODO: add a new service
|
||||
const config = getService('config');
|
||||
const ELASTICSEARCH_USERNAME = config.get('servers.kibana.username');
|
||||
const isServerless = config.get('serverless');
|
||||
const dataPathBuilder = new EsArchivePathBuilder(isServerless);
|
||||
const path = dataPathBuilder.getPath('auditbeat/hosts');
|
||||
/**
|
||||
* indexes 2 sets of documents:
|
||||
* - documents in historical window
|
||||
|
@ -72,14 +76,14 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
return testId;
|
||||
};
|
||||
|
||||
describe('New terms type rules', () => {
|
||||
describe('@ess @serverless New terms type rules', () => {
|
||||
before(async () => {
|
||||
await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/hosts');
|
||||
await esArchiver.load(path);
|
||||
await esArchiver.load('x-pack/test/functional/es_archives/security_solution/new_terms');
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/hosts');
|
||||
await esArchiver.unload(path);
|
||||
await esArchiver.unload('x-pack/test/functional/es_archives/security_solution/new_terms');
|
||||
await deleteAllAlerts(supertest, log, es);
|
||||
await deleteAllRules(supertest, log);
|
||||
|
@ -99,10 +103,10 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
};
|
||||
|
||||
const createdRule = await createRule(supertest, log, rule);
|
||||
const alerts = await getOpenSignals(supertest, log, es, createdRule);
|
||||
const alerts = await getOpenAlerts(supertest, log, es, createdRule);
|
||||
|
||||
expect(alerts.hits.hits.length).eql(1);
|
||||
expect(removeRandomValuedProperties(alerts.hits.hits[0]._source)).eql({
|
||||
expect(removeRandomValuedPropertiesFromAlert(alerts.hits.hits[0]._source)).eql({
|
||||
'kibana.alert.new_terms': ['zeek-newyork-sha-aa8df15'],
|
||||
'kibana.alert.rule.category': 'New Terms Rule',
|
||||
'kibana.alert.rule.consumer': 'siem',
|
||||
|
@ -196,7 +200,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
},
|
||||
'kibana.alert.rule.actions': [],
|
||||
'kibana.alert.rule.author': [],
|
||||
'kibana.alert.rule.created_by': 'elastic',
|
||||
'kibana.alert.rule.created_by': ELASTICSEARCH_USERNAME,
|
||||
'kibana.alert.rule.description': 'Detecting root and admin users',
|
||||
'kibana.alert.rule.enabled': true,
|
||||
'kibana.alert.rule.exceptions_list': [],
|
||||
|
@ -214,7 +218,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
'kibana.alert.rule.threat': [],
|
||||
'kibana.alert.rule.to': 'now',
|
||||
'kibana.alert.rule.type': 'new_terms',
|
||||
'kibana.alert.rule.updated_by': 'elastic',
|
||||
'kibana.alert.rule.updated_by': ELASTICSEARCH_USERNAME,
|
||||
'kibana.alert.rule.version': 1,
|
||||
'kibana.alert.rule.risk_score': 55,
|
||||
'kibana.alert.rule.severity': 'high',
|
||||
|
@ -229,7 +233,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
});
|
||||
});
|
||||
|
||||
it('generates max signals warning when circuit breaker is exceeded', async () => {
|
||||
it('generates max alerts warning when circuit breaker is exceeded', async () => {
|
||||
const rule: NewTermsRuleCreateProps = {
|
||||
...getCreateNewTermsRulesSchemaMock('rule-1', true),
|
||||
new_terms_fields: ['process.pid'],
|
||||
|
@ -239,10 +243,10 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
};
|
||||
const { logs } = await previewRule({ supertest, rule });
|
||||
|
||||
expect(logs[0].warnings).contain(getMaxSignalsWarning());
|
||||
expect(logs[0].warnings).contain(getMaxAlertsWarning());
|
||||
});
|
||||
|
||||
it("doesn't generate max signals warning when circuit breaker is met but not exceeded", async () => {
|
||||
it("doesn't generate max alerts warning when circuit breaker is met but not exceeded", async () => {
|
||||
const rule: NewTermsRuleCreateProps = {
|
||||
...getCreateNewTermsRulesSchemaMock('rule-1', true),
|
||||
new_terms_fields: ['host.ip'],
|
||||
|
@ -252,7 +256,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
};
|
||||
const { logs } = await previewRule({ supertest, rule });
|
||||
|
||||
expect(logs[0].warnings).not.contain(getMaxSignalsWarning());
|
||||
expect(logs[0].warnings).not.contain(getMaxAlertsWarning());
|
||||
});
|
||||
|
||||
it('should generate 3 alerts when 1 document has 3 new values', async () => {
|
||||
|
@ -990,21 +994,21 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should work for max signals > 100', async () => {
|
||||
const maxSignals = 200;
|
||||
it('should work for max alerts > 100', async () => {
|
||||
const maxAlerts = 200;
|
||||
const rule: NewTermsRuleCreateProps = {
|
||||
...getCreateNewTermsRulesSchemaMock('rule-1', true),
|
||||
new_terms_fields: ['process.pid'],
|
||||
from: '2018-02-19T20:42:00.000Z',
|
||||
// Set the history_window_start close to 'from' so we should alert on all terms in the time range
|
||||
history_window_start: '2018-02-19T20:41:59.000Z',
|
||||
max_signals: maxSignals,
|
||||
max_signals: maxAlerts,
|
||||
};
|
||||
|
||||
const { previewId } = await previewRule({ supertest, rule });
|
||||
const previewAlerts = await getPreviewAlerts({ es, previewId, size: maxSignals * 2 });
|
||||
const previewAlerts = await getPreviewAlerts({ es, previewId, size: maxAlerts * 2 });
|
||||
|
||||
expect(previewAlerts.length).eql(maxSignals);
|
||||
expect(previewAlerts.length).eql(maxAlerts);
|
||||
const processPids = previewAlerts
|
||||
.map((signal) => signal._source?.['kibana.alert.new_terms'])
|
||||
.sort();
|
|
@ -10,18 +10,18 @@ import {
|
|||
deleteAllRules,
|
||||
deleteAllAlerts,
|
||||
getPreviewAlerts,
|
||||
getRuleForSignalTesting,
|
||||
getRuleForAlertTesting,
|
||||
previewRule,
|
||||
} from '../../utils';
|
||||
import { dataGeneratorFactory, enhanceDocument } from '../../utils/data_generator';
|
||||
import { FtrProviderContext } from '../../common/ftr_provider_context';
|
||||
dataGeneratorFactory,
|
||||
enhanceDocument,
|
||||
} from '../../../utils';
|
||||
import { FtrProviderContext } from '../../../../../ftr_provider_context';
|
||||
|
||||
const getQueryRule = (docIdToQuery: string) => ({
|
||||
...getRuleForSignalTesting(['ecs_non_compliant']),
|
||||
...getRuleForAlertTesting(['ecs_non_compliant']),
|
||||
query: `id: "${docIdToQuery}"`,
|
||||
});
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default ({ getService }: FtrProviderContext) => {
|
||||
const supertest = getService('supertest');
|
||||
const esArchiver = getService('esArchiver');
|
||||
|
@ -56,7 +56,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
};
|
||||
};
|
||||
|
||||
describe('Non ECS fields in alert document source', () => {
|
||||
describe('@ess @serverless Non ECS fields in alert document source', () => {
|
||||
before(async () => {
|
||||
await esArchiver.load(
|
||||
'x-pack/test/functional/es_archives/security_solution/ecs_non_compliant'
|
|
@ -42,29 +42,32 @@ import {
|
|||
import {
|
||||
DETECTION_ENGINE_RULES_BULK_ACTION,
|
||||
DETECTION_ENGINE_RULES_URL,
|
||||
DETECTION_ENGINE_SIGNALS_STATUS_URL,
|
||||
DETECTION_ENGINE_SIGNALS_STATUS_URL as DETECTION_ENGINE_ALERTS_STATUS_URL,
|
||||
} from '@kbn/security-solution-plugin/common/constants';
|
||||
import { getMaxSignalsWarning } from '@kbn/security-solution-plugin/server/lib/detection_engine/rule_types/utils/utils';
|
||||
import { deleteAllExceptions } from '../../../lists_api_integration/utils';
|
||||
import { getMaxSignalsWarning as getMaxAlertsWarning } from '@kbn/security-solution-plugin/server/lib/detection_engine/rule_types/utils/utils';
|
||||
import moment from 'moment';
|
||||
import { deleteAllExceptions } from '../../../../../../lists_api_integration/utils';
|
||||
import {
|
||||
createExceptionList,
|
||||
createExceptionListItem,
|
||||
createRule,
|
||||
deleteAllRules,
|
||||
deleteAllAlerts,
|
||||
getOpenSignals,
|
||||
getOpenAlerts,
|
||||
getPreviewAlerts,
|
||||
getRuleForSignalTesting,
|
||||
getRuleForAlertTesting,
|
||||
getSimpleRule,
|
||||
previewRule,
|
||||
setSignalStatus,
|
||||
setAlertStatus,
|
||||
getRuleSOById,
|
||||
patchRule,
|
||||
createRuleThroughAlertingEndpoint,
|
||||
getRuleSavedObjectWithLegacyInvestigationFields,
|
||||
} from '../../utils';
|
||||
import { FtrProviderContext } from '../../common/ftr_provider_context';
|
||||
import { dataGeneratorFactory } from '../../utils/data_generator';
|
||||
import { patchRule } from '../../utils/patch_rule';
|
||||
dataGeneratorFactory,
|
||||
} from '../../../utils';
|
||||
|
||||
import { FtrProviderContext } from '../../../../../ftr_provider_context';
|
||||
import { EsArchivePathBuilder } from '../../../../../es_archive_path_builder';
|
||||
|
||||
/**
|
||||
* Specific _id to use for some of the tests. If the archiver changes and you see errors
|
||||
|
@ -75,22 +78,29 @@ const ID = 'BhbXBmkBR346wHgn4PeZ';
|
|||
/**
|
||||
* Test coverage:
|
||||
* [x] - Happy path generating 1 alert
|
||||
* [x] - Rule type respects max signals
|
||||
* [x] - Rule type respects max alerts
|
||||
* [x] - Alerts on alerts
|
||||
*/
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default ({ getService }: FtrProviderContext) => {
|
||||
const supertest = getService('supertest');
|
||||
const esArchiver = getService('esArchiver');
|
||||
const es = getService('es');
|
||||
const log = getService('log');
|
||||
const esDeleteAllIndices = getService('esDeleteAllIndices');
|
||||
// TODO: add a new service
|
||||
const config = getService('config');
|
||||
const isServerless = config.get('serverless');
|
||||
const dataPathBuilder = new EsArchivePathBuilder(isServerless);
|
||||
const auditbeatPath = dataPathBuilder.getPath('auditbeat/hosts');
|
||||
|
||||
describe('Query type rules', () => {
|
||||
describe('@ess @serverless Query type rules', () => {
|
||||
before(async () => {
|
||||
await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/hosts');
|
||||
await esArchiver.load('x-pack/test/functional/es_archives/security_solution/alerts/8.1.0');
|
||||
await esArchiver.load(auditbeatPath);
|
||||
await esArchiver.load('x-pack/test/functional/es_archives/security_solution/alerts/8.8.0', {
|
||||
useCreate: true,
|
||||
docsOnly: true,
|
||||
});
|
||||
await esArchiver.load('x-pack/test/functional/es_archives/signals/severity_risk_overrides');
|
||||
});
|
||||
|
||||
|
@ -99,8 +109,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
});
|
||||
|
||||
after(async () => {
|
||||
await esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/hosts');
|
||||
await esArchiver.unload('x-pack/test/functional/es_archives/security_solution/alerts/8.1.0');
|
||||
await esArchiver.unload(auditbeatPath);
|
||||
await esArchiver.unload('x-pack/test/functional/es_archives/signals/severity_risk_overrides');
|
||||
await deleteAllAlerts(supertest, log, es, ['.preview.alerts-security.alerts-*']);
|
||||
await deleteAllRules(supertest, log);
|
||||
|
@ -109,48 +118,48 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
// First test creates a real rule - most remaining tests use preview API
|
||||
it('should have the specific audit record for _id or none of these tests below will pass', async () => {
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['auditbeat-*']),
|
||||
...getRuleForAlertTesting(['auditbeat-*']),
|
||||
query: `_id:${ID}`,
|
||||
};
|
||||
const createdRule = await createRule(supertest, log, rule);
|
||||
const alerts = await getOpenSignals(supertest, log, es, createdRule);
|
||||
const alerts = await getOpenAlerts(supertest, log, es, createdRule);
|
||||
expect(alerts.hits.hits.length).greaterThan(0);
|
||||
expect(alerts.hits.hits[0]._source?.['kibana.alert.ancestors'][0].id).eql(ID);
|
||||
});
|
||||
|
||||
it('generates max signals warning when circuit breaker is hit', async () => {
|
||||
it('generates max alerts warning when circuit breaker is hit', async () => {
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['auditbeat-*']),
|
||||
...getRuleForAlertTesting(['auditbeat-*']),
|
||||
};
|
||||
const { logs } = await previewRule({ supertest, rule });
|
||||
expect(logs[0].warnings).contain(getMaxSignalsWarning());
|
||||
expect(logs[0].warnings).contain(getMaxAlertsWarning());
|
||||
});
|
||||
|
||||
it("doesn't generate max signals warning when circuit breaker is met but not exceeded", async () => {
|
||||
it("doesn't generate max alerts warning when circuit breaker is met but not exceeded", async () => {
|
||||
const rule = {
|
||||
...getRuleForSignalTesting(['auditbeat-*']),
|
||||
...getRuleForAlertTesting(['auditbeat-*']),
|
||||
query: 'process.executable: "/usr/bin/sudo"',
|
||||
max_signals: 10,
|
||||
};
|
||||
const { logs } = await previewRule({ supertest, rule });
|
||||
expect(logs[0].warnings).not.contain(getMaxSignalsWarning());
|
||||
expect(logs[0].warnings).not.contain(getMaxAlertsWarning());
|
||||
});
|
||||
|
||||
it('should abide by max_signals > 100', async () => {
|
||||
const maxSignals = 200;
|
||||
const maxAlerts = 200;
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['auditbeat-*']),
|
||||
max_signals: maxSignals,
|
||||
...getRuleForAlertTesting(['auditbeat-*']),
|
||||
max_signals: maxAlerts,
|
||||
};
|
||||
const { previewId } = await previewRule({ supertest, rule });
|
||||
// Search for 2x max_signals to make sure we aren't making more than max_signals
|
||||
const previewAlerts = await getPreviewAlerts({ es, previewId, size: maxSignals * 2 });
|
||||
expect(previewAlerts.length).equal(maxSignals);
|
||||
const previewAlerts = await getPreviewAlerts({ es, previewId, size: maxAlerts * 2 });
|
||||
expect(previewAlerts.length).equal(maxAlerts);
|
||||
});
|
||||
|
||||
it('should have recorded the rule_id within the signal', async () => {
|
||||
it('should have recorded the rule_id within the alert', async () => {
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['auditbeat-*']),
|
||||
...getRuleForAlertTesting(['auditbeat-*']),
|
||||
query: `_id:${ID}`,
|
||||
};
|
||||
const { previewId } = await previewRule({ supertest, rule });
|
||||
|
@ -158,17 +167,17 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
expect(previewAlerts[0]._source?.[ALERT_RULE_RULE_ID]).eql(getSimpleRule().rule_id);
|
||||
});
|
||||
|
||||
it('should query and get back expected signal structure using a basic KQL query', async () => {
|
||||
it('should query and get back expected alert structure using a basic KQL query', async () => {
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['auditbeat-*']),
|
||||
...getRuleForAlertTesting(['auditbeat-*']),
|
||||
query: `_id:${ID}`,
|
||||
};
|
||||
const { previewId } = await previewRule({ supertest, rule });
|
||||
const previewAlerts = await getPreviewAlerts({ es, previewId });
|
||||
const signal = previewAlerts[0]._source;
|
||||
const alert = previewAlerts[0]._source;
|
||||
|
||||
expect(signal).eql({
|
||||
...signal,
|
||||
expect(alert).eql({
|
||||
...alert,
|
||||
[ALERT_ANCESTORS]: [
|
||||
{
|
||||
id: 'BhbXBmkBR346wHgn4PeZ',
|
||||
|
@ -189,11 +198,11 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should query and get back expected signal structure when it is a signal on a signal', async () => {
|
||||
const alertId = '30a75fe46d3dbdfab55982036f77a8d60e2d1112e96f277c3b8c22f9bb57817a';
|
||||
it('should query and get back expected alert structure when it is a alert on a alert', async () => {
|
||||
const alertId = 'eabbdefc23da981f2b74ab58b82622a97bb9878caa11bc914e2adfacc94780f1';
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting([`.alerts-security.alerts-default*`]),
|
||||
rule_id: 'signal-on-signal',
|
||||
...getRuleForAlertTesting([`.alerts-security.alerts-default*`]),
|
||||
rule_id: 'alert-on-alert',
|
||||
query: `_id:${alertId}`,
|
||||
};
|
||||
|
||||
|
@ -202,43 +211,44 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
|
||||
expect(previewAlerts.length).to.eql(1);
|
||||
|
||||
const signal = previewAlerts[0]._source;
|
||||
const alert = previewAlerts[0]._source;
|
||||
|
||||
if (!signal) {
|
||||
return expect(signal).to.be.ok();
|
||||
if (!alert) {
|
||||
return expect(alert).to.be.ok();
|
||||
}
|
||||
const date = moment();
|
||||
const formattedDate = date.format('YYYY.MM.DD');
|
||||
const alertAncestorIndex = isServerless
|
||||
? `.ds-.alerts-security.alerts-default-${formattedDate}-000001`
|
||||
: '.internal.alerts-security.alerts-default-000001';
|
||||
expect(alert[ALERT_ANCESTORS]).eql([
|
||||
{
|
||||
id: 'vT9cwocBh3b8EMpD8lsi',
|
||||
type: 'event',
|
||||
index: '.ds-logs-endpoint.alerts-default-2023.04.27-000001',
|
||||
depth: 0,
|
||||
},
|
||||
{
|
||||
rule: '7015a3e2-e4ea-11ed-8c11-49608884878f',
|
||||
id: alertId,
|
||||
type: 'signal',
|
||||
index: alertAncestorIndex,
|
||||
depth: 1,
|
||||
},
|
||||
]);
|
||||
expect(alert[ALERT_WORKFLOW_STATUS]).eql('open');
|
||||
expect(alert[ALERT_DEPTH]).eql(2);
|
||||
|
||||
expect(signal).eql({
|
||||
...signal,
|
||||
[ALERT_ANCESTORS]: [
|
||||
{
|
||||
id: 'ahEToH8BK09aFtXZFVMq',
|
||||
type: 'event',
|
||||
index: 'events-index-000001',
|
||||
depth: 0,
|
||||
},
|
||||
{
|
||||
rule: '031d5c00-a72f-11ec-a8a3-7b1c8077fc3e',
|
||||
id: '30a75fe46d3dbdfab55982036f77a8d60e2d1112e96f277c3b8c22f9bb57817a',
|
||||
type: 'signal',
|
||||
index: '.internal.alerts-security.alerts-default-000001',
|
||||
depth: 1,
|
||||
},
|
||||
],
|
||||
[ALERT_WORKFLOW_STATUS]: 'open',
|
||||
[ALERT_DEPTH]: 2,
|
||||
[ALERT_ORIGINAL_TIME]: '2022-03-19T02:48:12.634Z',
|
||||
...flattenWithPrefix(ALERT_ORIGINAL_EVENT, {
|
||||
agent_id_status: 'verified',
|
||||
ingested: '2022-03-19T02:47:57.376Z',
|
||||
dataset: 'elastic_agent.filebeat',
|
||||
}),
|
||||
});
|
||||
expect(alert[ALERT_ORIGINAL_TIME]).eql('2023-04-27T11:03:57.906Z');
|
||||
expect(alert[`${ALERT_ORIGINAL_EVENT}.agent_id_status`]).eql('auth_metadata_missing');
|
||||
expect(alert[`${ALERT_ORIGINAL_EVENT}.ingested`]).eql('2023-04-27T10:58:03Z');
|
||||
expect(alert[`${ALERT_ORIGINAL_EVENT}.dataset`]).eql('endpoint');
|
||||
expect(alert[`${ALERT_ORIGINAL_EVENT}.ingested`]).eql('2023-04-27T10:58:03Z');
|
||||
});
|
||||
|
||||
it('should not have risk score fields without risk indices', async () => {
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['auditbeat-*']),
|
||||
...getRuleForAlertTesting(['auditbeat-*']),
|
||||
query: `_id:${ID}`,
|
||||
};
|
||||
const { previewId } = await previewRule({ supertest, rule });
|
||||
|
@ -258,7 +268,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
|
||||
it('should have host and user risk score fields', async () => {
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['auditbeat-*']),
|
||||
...getRuleForAlertTesting(['auditbeat-*']),
|
||||
query: `_id:${ID}`,
|
||||
};
|
||||
const { previewId } = await previewRule({ supertest, rule });
|
||||
|
@ -273,11 +283,11 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
/**
|
||||
* Here we test the functionality of Severity and Risk Score overrides (also called "mappings"
|
||||
* in the code). If the rule specifies a mapping, then the final Severity or Risk Score
|
||||
* value of the signal will be taken from the mapped field of the source event.
|
||||
* value of the alert will be taken from the mapped field of the source event.
|
||||
*/
|
||||
it('should get default severity and risk score if there is no mapping', async () => {
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['signal_overrides']),
|
||||
...getRuleForAlertTesting(['signal_overrides']),
|
||||
severity: 'medium',
|
||||
risk_score: 75,
|
||||
};
|
||||
|
@ -297,7 +307,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
|
||||
it('should get overridden severity if the rule has a mapping for it', async () => {
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['signal_overrides']),
|
||||
...getRuleForAlertTesting(['signal_overrides']),
|
||||
severity: 'medium',
|
||||
severity_mapping: [
|
||||
{ field: 'my_severity', operator: 'equals', value: 'sev_900', severity: 'high' },
|
||||
|
@ -334,7 +344,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
|
||||
it('should get overridden risk score if the rule has a mapping for it', async () => {
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['signal_overrides']),
|
||||
...getRuleForAlertTesting(['signal_overrides']),
|
||||
severity: 'medium',
|
||||
risk_score: 75,
|
||||
risk_score_mapping: [
|
||||
|
@ -369,7 +379,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
|
||||
it('should get overridden severity and risk score if the rule has both mappings', async () => {
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['signal_overrides']),
|
||||
...getRuleForAlertTesting(['signal_overrides']),
|
||||
severity: 'medium',
|
||||
severity_mapping: [
|
||||
{ field: 'my_severity', operator: 'equals', value: 'sev_900', severity: 'high' },
|
||||
|
@ -409,26 +419,26 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should generate signals with name_override field', async () => {
|
||||
it('should generate alerts with name_override field', async () => {
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['auditbeat-*']),
|
||||
...getRuleForAlertTesting(['auditbeat-*']),
|
||||
query: `event.action:boot`,
|
||||
rule_name_override: 'event.action',
|
||||
};
|
||||
|
||||
const { previewId } = await previewRule({ supertest, rule });
|
||||
const previewAlerts = await getPreviewAlerts({ es, previewId });
|
||||
const fullSignal = previewAlerts[0];
|
||||
if (!fullSignal) {
|
||||
return expect(fullSignal).to.be.ok();
|
||||
const fullAlert = previewAlerts[0];
|
||||
if (!fullAlert) {
|
||||
return expect(fullAlert).to.be.ok();
|
||||
}
|
||||
|
||||
expect(previewAlerts[0]._source?.['kibana.alert.rule.name']).to.eql('boot');
|
||||
});
|
||||
|
||||
it('should not generate duplicate signals', async () => {
|
||||
it('should not generate duplicate alerts', async () => {
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['auditbeat-*']),
|
||||
...getRuleForAlertTesting(['auditbeat-*']),
|
||||
query: `_id:${ID}`,
|
||||
};
|
||||
|
||||
|
@ -448,7 +458,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
|
||||
it('should generate only 1 alert per host name when grouping by host name', async () => {
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['suppression-data']),
|
||||
...getRuleForAlertTesting(['suppression-data']),
|
||||
query: `host.name: "host-0"`,
|
||||
alert_suppression: {
|
||||
group_by: ['host.name'],
|
||||
|
@ -481,7 +491,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
|
||||
it('should generate multiple alerts when multiple host names are found', async () => {
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['suppression-data']),
|
||||
...getRuleForAlertTesting(['suppression-data']),
|
||||
query: `host.name: *`,
|
||||
alert_suppression: {
|
||||
group_by: ['host.name'],
|
||||
|
@ -521,7 +531,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
|
||||
it('should generate alerts when using multiple group by fields', async () => {
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['suppression-data']),
|
||||
...getRuleForAlertTesting(['suppression-data']),
|
||||
query: `host.name: *`,
|
||||
alert_suppression: {
|
||||
group_by: ['host.name', 'source.ip'],
|
||||
|
@ -564,7 +574,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
|
||||
it('should not count documents that were covered by previous alerts', async () => {
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['suppression-data']),
|
||||
...getRuleForAlertTesting(['suppression-data']),
|
||||
query: `host.name: *`,
|
||||
alert_suppression: {
|
||||
group_by: ['host.name', 'source.ip'],
|
||||
|
@ -632,7 +642,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
// so we expect 2 groups to be created from the single document
|
||||
it('should generate multiple alerts for a single doc in multiple groups', async () => {
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['suppression-data']),
|
||||
...getRuleForAlertTesting(['suppression-data']),
|
||||
query: `*:*`,
|
||||
alert_suppression: {
|
||||
group_by: ['destination.ip'],
|
||||
|
@ -689,7 +699,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
// The last alert, with null for destination.ip, should be found by the first rule run but not duplicated
|
||||
// by the second run.
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['suppression-data']),
|
||||
...getRuleForAlertTesting(['suppression-data']),
|
||||
query: `*:*`,
|
||||
alert_suppression: {
|
||||
group_by: ['destination.ip'],
|
||||
|
@ -760,7 +770,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
await indexListOfDocuments([firstDocument, firstDocument]);
|
||||
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['ecs_compliant']),
|
||||
...getRuleForAlertTesting(['ecs_compliant']),
|
||||
rule_id: 'rule-2',
|
||||
query: `id:${id}`,
|
||||
alert_suppression: {
|
||||
|
@ -772,7 +782,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
},
|
||||
};
|
||||
const createdRule = await createRule(supertest, log, rule);
|
||||
const alerts = await getOpenSignals(supertest, log, es, createdRule);
|
||||
const alerts = await getOpenAlerts(supertest, log, es, createdRule);
|
||||
expect(alerts.hits.hits.length).eql(1);
|
||||
expect(alerts.hits.hits[0]._source).to.eql({
|
||||
...alerts.hits.hits[0]._source,
|
||||
|
@ -802,7 +812,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
await patchRule(supertest, log, { id: createdRule.id, enabled: false });
|
||||
await patchRule(supertest, log, { id: createdRule.id, enabled: true });
|
||||
const afterTimestamp = new Date();
|
||||
const secondAlerts = await getOpenSignals(
|
||||
const secondAlerts = await getOpenAlerts(
|
||||
supertest,
|
||||
log,
|
||||
es,
|
||||
|
@ -841,7 +851,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
await indexListOfDocuments([firstDocument, firstDocument]);
|
||||
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['ecs_compliant']),
|
||||
...getRuleForAlertTesting(['ecs_compliant']),
|
||||
rule_id: 'rule-2',
|
||||
query: `id:${id}`,
|
||||
alert_suppression: {
|
||||
|
@ -853,15 +863,15 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
},
|
||||
};
|
||||
const createdRule = await createRule(supertest, log, rule);
|
||||
const alerts = await getOpenSignals(supertest, log, es, createdRule);
|
||||
const alerts = await getOpenAlerts(supertest, log, es, createdRule);
|
||||
|
||||
// Close the alert. Subsequent rule executions should ignore this closed alert
|
||||
// for suppression purposes.
|
||||
const alertIds = alerts.hits.hits.map((alert) => alert._id);
|
||||
await supertest
|
||||
.post(DETECTION_ENGINE_SIGNALS_STATUS_URL)
|
||||
.post(DETECTION_ENGINE_ALERTS_STATUS_URL)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send(setSignalStatus({ signalIds: alertIds, status: 'closed' }))
|
||||
.send(setAlertStatus({ alertIds, status: 'closed' }))
|
||||
.expect(200);
|
||||
|
||||
const secondTimestamp = new Date().toISOString();
|
||||
|
@ -878,7 +888,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
await patchRule(supertest, log, { id: createdRule.id, enabled: false });
|
||||
await patchRule(supertest, log, { id: createdRule.id, enabled: true });
|
||||
const afterTimestamp = new Date();
|
||||
const secondAlerts = await getOpenSignals(
|
||||
const secondAlerts = await getOpenAlerts(
|
||||
supertest,
|
||||
log,
|
||||
es,
|
||||
|
@ -922,7 +932,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
|
||||
it('should generate an alert per rule run when duration is less than rule interval', async () => {
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['suppression-data']),
|
||||
...getRuleForAlertTesting(['suppression-data']),
|
||||
query: `host.name: "host-0"`,
|
||||
alert_suppression: {
|
||||
group_by: ['host.name'],
|
||||
|
@ -981,7 +991,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
|
||||
it('should update an existing alert in the time window', async () => {
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['suppression-data']),
|
||||
...getRuleForAlertTesting(['suppression-data']),
|
||||
query: `host.name: "host-0"`,
|
||||
alert_suppression: {
|
||||
group_by: ['host.name'],
|
||||
|
@ -1025,7 +1035,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
|
||||
it('should update the correct alerts based on group_by field-value pair', async () => {
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['suppression-data']),
|
||||
...getRuleForAlertTesting(['suppression-data']),
|
||||
query: `host.name: *`,
|
||||
alert_suppression: {
|
||||
group_by: ['host.name'],
|
||||
|
@ -1099,7 +1109,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
|
||||
it('should update the correct alerts based on group_by field-value pair even when value is null', async () => {
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['suppression-data']),
|
||||
...getRuleForAlertTesting(['suppression-data']),
|
||||
query: `host.name: *`,
|
||||
alert_suppression: {
|
||||
group_by: ['destination.ip'], // Only 1 document populates destination.ip
|
||||
|
@ -1162,7 +1172,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
await indexListOfDocuments([docWithoutOverride, docWithOverride]);
|
||||
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['ecs_compliant']),
|
||||
...getRuleForAlertTesting(['ecs_compliant']),
|
||||
query: `id:${id}`,
|
||||
alert_suppression: {
|
||||
group_by: ['agent.name'],
|
||||
|
@ -1226,7 +1236,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
);
|
||||
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['ecs_compliant']),
|
||||
...getRuleForAlertTesting(['ecs_compliant']),
|
||||
query: `id:${id}`,
|
||||
alert_suppression: {
|
||||
group_by: ['agent.name'],
|
||||
|
@ -1313,7 +1323,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
await indexListOfDocuments([firstDoc, secondDoc, thirdDoc]);
|
||||
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['ecs_compliant']),
|
||||
...getRuleForAlertTesting(['ecs_compliant']),
|
||||
query: `id:${id}`,
|
||||
alert_suppression: {
|
||||
group_by: ['agent.name'],
|
||||
|
@ -1383,7 +1393,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
|
||||
it('should be enriched with host risk score', async () => {
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['suppression-data']),
|
||||
...getRuleForAlertTesting(['suppression-data']),
|
||||
query: `host.name: "host-0"`,
|
||||
alert_suppression: {
|
||||
group_by: ['host.name'],
|
||||
|
@ -1474,7 +1484,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
]);
|
||||
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['ecs_compliant']),
|
||||
...getRuleForAlertTesting(['ecs_compliant']),
|
||||
query: `id:${id}`,
|
||||
alert_suppression: {
|
||||
group_by: ['agent.name'],
|
||||
|
@ -1545,7 +1555,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
]);
|
||||
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['ecs_compliant']),
|
||||
...getRuleForAlertTesting(['ecs_compliant']),
|
||||
query: `id:${id}`,
|
||||
alert_suppression: {
|
||||
group_by: ['agent.name'],
|
||||
|
@ -1600,7 +1610,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
await indexListOfDocuments([firstDoc, firstDoc]);
|
||||
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['ecs_compliant']),
|
||||
...getRuleForAlertTesting(['ecs_compliant']),
|
||||
query: `id:${id}`,
|
||||
alert_suppression: {
|
||||
group_by: ['agent.name'],
|
||||
|
@ -1654,7 +1664,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
await indexListOfDocuments([firstDoc, firstDoc]);
|
||||
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['ecs_compliant']),
|
||||
...getRuleForAlertTesting(['ecs_compliant']),
|
||||
query: `id:${id}`,
|
||||
alert_suppression: {
|
||||
group_by: ['agent.name'],
|
||||
|
@ -1752,7 +1762,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
]);
|
||||
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['ecs_compliant']),
|
||||
...getRuleForAlertTesting(['ecs_compliant']),
|
||||
query: `id:${id}`,
|
||||
alert_suppression: {
|
||||
group_by: ['agent.name', 'agent.version'],
|
||||
|
@ -1831,7 +1841,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
]);
|
||||
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['ecs_compliant']),
|
||||
...getRuleForAlertTesting(['ecs_compliant']),
|
||||
query: `id:${id}`,
|
||||
alert_suppression: {
|
||||
group_by: ['agent.name', 'agent.version'],
|
||||
|
@ -1898,7 +1908,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
});
|
||||
it('should create suppressed alerts for single host.name when rule configure with suppress', async () => {
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['ecs_compliant']),
|
||||
...getRuleForAlertTesting(['ecs_compliant']),
|
||||
query: `id:${id}`,
|
||||
alert_suppression: {
|
||||
group_by: ['agent.name'],
|
||||
|
@ -1965,7 +1975,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
|
||||
it('should create unsuppressed alerts for single host.name', async () => {
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['ecs_compliant']),
|
||||
...getRuleForAlertTesting(['ecs_compliant']),
|
||||
query: `id:${id}`,
|
||||
alert_suppression: {
|
||||
group_by: ['agent.name'],
|
||||
|
@ -2076,7 +2086,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
});
|
||||
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['auditbeat-*']),
|
||||
...getRuleForAlertTesting(['auditbeat-*']),
|
||||
query: `_id:${ID} or _id:GBbXBmkBR346wHgn5_eR or _id:x10zJ2oE9v5HJNSHhyxi`,
|
||||
exceptions_list: [{ id, list_id: listId, type, namespace_type: namespaceType }],
|
||||
};
|
||||
|
@ -2119,7 +2129,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
});
|
||||
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['auditbeat-*']),
|
||||
...getRuleForAlertTesting(['auditbeat-*']),
|
||||
query: `_id:${ID} or _id:GBbXBmkBR346wHgn5_eR or _id:x10zJ2oE9v5HJNSHhyxi`,
|
||||
exceptions_list: [{ id, list_id: listId, type, namespace_type: namespaceType }],
|
||||
};
|
||||
|
@ -2157,7 +2167,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
await indexEnhancedDocuments({ documents: [firstDoc, firstDoc, secondDoc], id });
|
||||
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['ecs_compliant']),
|
||||
...getRuleForAlertTesting(['ecs_compliant']),
|
||||
query: `id:${id} AND agent.n*: test-1`,
|
||||
from: 'now-1h',
|
||||
interval: '1h',
|
||||
|
@ -2188,7 +2198,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
await indexEnhancedDocuments({ documents: [firstDoc, firstDoc, secondDoc], id });
|
||||
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['ecs_compliant']),
|
||||
...getRuleForAlertTesting(['ecs_compliant']),
|
||||
query: `id:${id} AND NOT agent.na*: "test-1"`,
|
||||
from: 'now-1h',
|
||||
interval: '1h',
|
||||
|
@ -2215,7 +2225,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
await indexEnhancedDocuments({ documents: [firstDoc, secondDoc, thirdDoc], id });
|
||||
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['ecs_compliant']),
|
||||
...getRuleForAlertTesting(['ecs_compliant']),
|
||||
query: `id:${id} AND agent*: "test-1"`,
|
||||
from: 'now-1h',
|
||||
interval: '1h',
|
||||
|
@ -2249,7 +2259,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
await indexEnhancedDocuments({ documents: [firstDoc, secondDoc, thirdDoc], id });
|
||||
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['ecs_compliant']),
|
||||
...getRuleForAlertTesting(['ecs_compliant']),
|
||||
query: `id:${id} AND agent.\\*: test-1`,
|
||||
from: 'now-1h',
|
||||
interval: '1h',
|
||||
|
@ -2276,7 +2286,8 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('legacy investigation_fields', () => {
|
||||
// TODO: Ask YARA
|
||||
describe('@brokenInServerless legacy investigation_fields', () => {
|
||||
let ruleWithLegacyInvestigationField: Rule<BaseRuleParams>;
|
||||
|
||||
beforeEach(async () => {
|
||||
|
@ -2317,7 +2328,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
.set('elastic-api-version', '2023-10-31')
|
||||
.expect(200);
|
||||
|
||||
const alertsAfterEnable = await getOpenSignals(supertest, log, es, ruleBody, 'succeeded');
|
||||
const alertsAfterEnable = await getOpenAlerts(supertest, log, es, ruleBody, 'succeeded');
|
||||
expect(alertsAfterEnable.hits.hits.length > 0).eql(true);
|
||||
});
|
||||
});
|
|
@ -20,10 +20,11 @@ import {
|
|||
createRule,
|
||||
deleteAllRules,
|
||||
deleteAllAlerts,
|
||||
getOpenSignals,
|
||||
getRuleForSignalTesting,
|
||||
} from '../../utils';
|
||||
import { FtrProviderContext } from '../../common/ftr_provider_context';
|
||||
getOpenAlerts,
|
||||
getRuleForAlertTesting,
|
||||
} from '../../../utils';
|
||||
import { FtrProviderContext } from '../../../../../ftr_provider_context';
|
||||
import { EsArchivePathBuilder } from '../../../../../es_archive_path_builder';
|
||||
|
||||
/**
|
||||
* Specific _id to use for some of the tests. If the archiver changes and you see errors
|
||||
|
@ -31,37 +32,41 @@ import { FtrProviderContext } from '../../common/ftr_provider_context';
|
|||
*/
|
||||
const ID = 'BhbXBmkBR346wHgn4PeZ';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default ({ getService }: FtrProviderContext) => {
|
||||
const supertest = getService('supertest');
|
||||
const esArchiver = getService('esArchiver');
|
||||
const es = getService('es');
|
||||
const log = getService('log');
|
||||
// TODO: add a new service
|
||||
const config = getService('config');
|
||||
const isServerless = config.get('serverless');
|
||||
const dataPathBuilder = new EsArchivePathBuilder(isServerless);
|
||||
const path = dataPathBuilder.getPath('auditbeat/hosts');
|
||||
|
||||
describe('Saved query type rules', () => {
|
||||
describe('@ess @serverless Saved query type rules', () => {
|
||||
before(async () => {
|
||||
await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/hosts');
|
||||
await esArchiver.load(path);
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/hosts');
|
||||
await esArchiver.unload(path);
|
||||
await deleteAllAlerts(supertest, log, es);
|
||||
await deleteAllRules(supertest, log);
|
||||
});
|
||||
|
||||
// First test creates a real rule - remaining tests use preview API
|
||||
it('should query and get back expected signal structure using a saved query rule', async () => {
|
||||
it('should query and get back expected alert structure using a saved query rule', async () => {
|
||||
const rule: SavedQueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['auditbeat-*']),
|
||||
...getRuleForAlertTesting(['auditbeat-*']),
|
||||
type: 'saved_query',
|
||||
query: `_id:${ID}`,
|
||||
saved_id: 'doesnt-exist',
|
||||
};
|
||||
const createdRule = await createRule(supertest, log, rule);
|
||||
const alerts = await getOpenSignals(supertest, log, es, createdRule);
|
||||
const signal = alerts.hits.hits[0]._source;
|
||||
expect(signal).eql({
|
||||
...signal,
|
||||
const alerts = await getOpenAlerts(supertest, log, es, createdRule);
|
||||
const alert = alerts.hits.hits[0]._source;
|
||||
expect(alert).eql({
|
||||
...alert,
|
||||
[ALERT_ANCESTORS]: [
|
||||
{
|
||||
id: 'BhbXBmkBR346wHgn4PeZ',
|
|
@ -35,16 +35,18 @@ import {
|
|||
ALERT_ORIGINAL_TIME,
|
||||
} from '@kbn/security-solution-plugin/common/field_maps/field_names';
|
||||
import { RuleExecutionStatusEnum } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_monitoring';
|
||||
import { getMaxSignalsWarning } from '@kbn/security-solution-plugin/server/lib/detection_engine/rule_types/utils/utils';
|
||||
import { getMaxSignalsWarning as getMaxAlertsWarning } from '@kbn/security-solution-plugin/server/lib/detection_engine/rule_types/utils/utils';
|
||||
import {
|
||||
previewRule,
|
||||
getOpenSignals,
|
||||
getOpenAlerts,
|
||||
getPreviewAlerts,
|
||||
deleteAllAlerts,
|
||||
deleteAllRules,
|
||||
createRule,
|
||||
} from '../../utils';
|
||||
import { FtrProviderContext } from '../../common/ftr_provider_context';
|
||||
} from '../../../utils';
|
||||
import { FtrProviderContext } from '../../../../../ftr_provider_context';
|
||||
import { EsArchivePathBuilder } from '../../../../../es_archive_path_builder';
|
||||
|
||||
const format = (value: unknown): string => JSON.stringify(value, null, 2);
|
||||
|
||||
// Asserts that each expected value is included in the subject, independent of
|
||||
|
@ -108,8 +110,8 @@ const createThreatMatchRule = ({
|
|||
threat_index,
|
||||
threat_mapping,
|
||||
threat_filters: [],
|
||||
threat_indicator_path,
|
||||
...override,
|
||||
threat_indicator_path,
|
||||
});
|
||||
|
||||
function alertsAreTheSame(alertsA: any[], alertsB: any[]): void {
|
||||
|
@ -130,6 +132,7 @@ function alertsAreTheSame(alertsA: any[], alertsB: any[]): void {
|
|||
'kibana.alert.start',
|
||||
'kibana.alert.reason',
|
||||
'kibana.alert.uuid',
|
||||
'kibana.alert.url',
|
||||
]);
|
||||
};
|
||||
|
||||
|
@ -138,37 +141,40 @@ function alertsAreTheSame(alertsA: any[], alertsB: any[]): void {
|
|||
|
||||
expect(sort(alertsA.map(mapAlert))).to.eql(sort(alertsB.map(mapAlert)));
|
||||
}
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default ({ getService }: FtrProviderContext) => {
|
||||
const esArchiver = getService('esArchiver');
|
||||
const supertest = getService('supertest');
|
||||
const es = getService('es');
|
||||
const log = getService('log');
|
||||
// TODO: add a new service
|
||||
const config = getService('config');
|
||||
const isServerless = config.get('serverless');
|
||||
const ELASTICSEARCH_USERNAME = config.get('servers.kibana.username');
|
||||
const dataPathBuilder = new EsArchivePathBuilder(isServerless);
|
||||
const audibeatHostsPath = dataPathBuilder.getPath('auditbeat/hosts');
|
||||
const threatIntelPath = dataPathBuilder.getPath('filebeat/threat_intel');
|
||||
|
||||
/**
|
||||
* Specific api integration tests for threat matching rule type
|
||||
*/
|
||||
// FLAKY: https://github.com/elastic/kibana/issues/155304
|
||||
describe('Threat match type rules', () => {
|
||||
describe('@ess @serverless Threat match type rules', () => {
|
||||
before(async () => {
|
||||
// await deleteSignalsIndex(supertest, log);
|
||||
// await deleteAllAlerts(supertest, log);
|
||||
await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/hosts');
|
||||
await esArchiver.load(audibeatHostsPath);
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/hosts');
|
||||
await esArchiver.unload(audibeatHostsPath);
|
||||
await deleteAllAlerts(supertest, log, es);
|
||||
await deleteAllRules(supertest, log);
|
||||
});
|
||||
|
||||
// First 2 test creates a real rule - remaining tests use preview API
|
||||
it('should be able to execute and get all signals when doing a specific query (terms query)', async () => {
|
||||
it('should be able to execute and get all alerts when doing a specific query (terms query)', async () => {
|
||||
const rule: ThreatMatchRuleCreateProps = createThreatMatchRule();
|
||||
|
||||
const createdRule = await createRule(supertest, log, rule);
|
||||
const alerts = await getOpenSignals(
|
||||
const alerts = await getOpenAlerts(
|
||||
supertest,
|
||||
log,
|
||||
es,
|
||||
|
@ -178,16 +184,15 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
);
|
||||
expect(alerts.hits.hits.length).equal(88);
|
||||
const fullSource = alerts.hits.hits.find(
|
||||
(signal) =>
|
||||
(signal._source?.[ALERT_ANCESTORS] as Ancestor[])[0].id === '7yJ-B2kBR346wHgnhlMn'
|
||||
(alert) => (alert._source?.[ALERT_ANCESTORS] as Ancestor[])[0].id === '7yJ-B2kBR346wHgnhlMn'
|
||||
);
|
||||
const fullSignal = fullSource?._source;
|
||||
if (!fullSignal) {
|
||||
return expect(fullSignal).to.be.ok();
|
||||
const fullAlert = fullSource?._source;
|
||||
if (!fullAlert) {
|
||||
return expect(fullAlert).to.be.ok();
|
||||
}
|
||||
expect(fullSignal).eql({
|
||||
...fullSignal,
|
||||
'@timestamp': fullSignal['@timestamp'],
|
||||
expect(fullAlert).eql({
|
||||
...fullAlert,
|
||||
'@timestamp': fullAlert['@timestamp'],
|
||||
agent: {
|
||||
ephemeral_id: '1b4978a0-48be-49b1-ac96-323425b389ab',
|
||||
hostname: 'zeek-sensor-amsterdam',
|
||||
|
@ -281,25 +286,25 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
[ALERT_ORIGINAL_EVENT_ACTION]: 'error',
|
||||
[ALERT_ORIGINAL_EVENT_CATEGORY]: 'user-login',
|
||||
[ALERT_ORIGINAL_EVENT_MODULE]: 'auditd',
|
||||
[ALERT_ORIGINAL_TIME]: fullSignal[ALERT_ORIGINAL_TIME],
|
||||
[ALERT_ORIGINAL_TIME]: fullAlert[ALERT_ORIGINAL_TIME],
|
||||
[ALERT_REASON]:
|
||||
'user-login event with source 46.101.47.213 by root on zeek-sensor-amsterdam created high alert Query with a rule id.',
|
||||
[ALERT_RULE_UUID]: fullSignal[ALERT_RULE_UUID],
|
||||
[ALERT_RULE_UUID]: fullAlert[ALERT_RULE_UUID],
|
||||
[ALERT_STATUS]: 'active',
|
||||
[ALERT_UUID]: fullSignal[ALERT_UUID],
|
||||
[ALERT_UUID]: fullAlert[ALERT_UUID],
|
||||
[ALERT_WORKFLOW_STATUS]: 'open',
|
||||
[ALERT_WORKFLOW_TAGS]: [],
|
||||
[SPACE_IDS]: ['default'],
|
||||
[VERSION]: fullSignal[VERSION],
|
||||
[VERSION]: fullAlert[VERSION],
|
||||
threat: {
|
||||
enrichments: get(fullSignal, 'threat.enrichments'),
|
||||
enrichments: get(fullAlert, 'threat.enrichments'),
|
||||
},
|
||||
...flattenWithPrefix(ALERT_RULE_NAMESPACE, {
|
||||
actions: [],
|
||||
author: [],
|
||||
category: 'Indicator Match Rule',
|
||||
consumer: 'siem',
|
||||
created_by: 'elastic',
|
||||
created_by: ELASTICSEARCH_USERNAME,
|
||||
description: 'Detecting root and admin users',
|
||||
enabled: true,
|
||||
exceptions_list: [],
|
||||
|
@ -320,14 +325,14 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
threat: [],
|
||||
to: 'now',
|
||||
type: 'threat_match',
|
||||
updated_at: fullSignal[ALERT_RULE_UPDATED_AT],
|
||||
updated_by: 'elastic',
|
||||
uuid: fullSignal[ALERT_RULE_UUID],
|
||||
updated_at: fullAlert[ALERT_RULE_UPDATED_AT],
|
||||
updated_by: ELASTICSEARCH_USERNAME,
|
||||
uuid: fullAlert[ALERT_RULE_UUID],
|
||||
version: 1,
|
||||
}),
|
||||
});
|
||||
});
|
||||
it('should be able to execute and get all signals when doing a specific query (match query)', async () => {
|
||||
it('should be able to execute and get all alerts when doing a specific query (match query)', async () => {
|
||||
const rule: ThreatMatchRuleCreateProps = createThreatMatchRule({
|
||||
threat_mapping: [
|
||||
// We match host.name against host.name
|
||||
|
@ -349,7 +354,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
});
|
||||
|
||||
const createdRule = await createRule(supertest, log, rule);
|
||||
const alerts = await getOpenSignals(
|
||||
const alerts = await getOpenAlerts(
|
||||
supertest,
|
||||
log,
|
||||
es,
|
||||
|
@ -359,16 +364,15 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
);
|
||||
expect(alerts.hits.hits.length).equal(88);
|
||||
const fullSource = alerts.hits.hits.find(
|
||||
(signal) =>
|
||||
(signal._source?.[ALERT_ANCESTORS] as Ancestor[])[0].id === '7yJ-B2kBR346wHgnhlMn'
|
||||
(alert) => (alert._source?.[ALERT_ANCESTORS] as Ancestor[])[0].id === '7yJ-B2kBR346wHgnhlMn'
|
||||
);
|
||||
const fullSignal = fullSource?._source;
|
||||
if (!fullSignal) {
|
||||
return expect(fullSignal).to.be.ok();
|
||||
const fullAlert = fullSource?._source;
|
||||
if (!fullAlert) {
|
||||
return expect(fullAlert).to.be.ok();
|
||||
}
|
||||
expect(fullSignal).eql({
|
||||
...fullSignal,
|
||||
'@timestamp': fullSignal['@timestamp'],
|
||||
expect(fullAlert).eql({
|
||||
...fullAlert,
|
||||
'@timestamp': fullAlert['@timestamp'],
|
||||
agent: {
|
||||
ephemeral_id: '1b4978a0-48be-49b1-ac96-323425b389ab',
|
||||
hostname: 'zeek-sensor-amsterdam',
|
||||
|
@ -462,24 +466,24 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
[ALERT_ORIGINAL_EVENT_ACTION]: 'error',
|
||||
[ALERT_ORIGINAL_EVENT_CATEGORY]: 'user-login',
|
||||
[ALERT_ORIGINAL_EVENT_MODULE]: 'auditd',
|
||||
[ALERT_ORIGINAL_TIME]: fullSignal[ALERT_ORIGINAL_TIME],
|
||||
[ALERT_ORIGINAL_TIME]: fullAlert[ALERT_ORIGINAL_TIME],
|
||||
[ALERT_REASON]:
|
||||
'user-login event with source 46.101.47.213 by root on zeek-sensor-amsterdam created high alert Query with a rule id.',
|
||||
[ALERT_RULE_UUID]: fullSignal[ALERT_RULE_UUID],
|
||||
[ALERT_RULE_UUID]: fullAlert[ALERT_RULE_UUID],
|
||||
[ALERT_STATUS]: 'active',
|
||||
[ALERT_UUID]: fullSignal[ALERT_UUID],
|
||||
[ALERT_UUID]: fullAlert[ALERT_UUID],
|
||||
[ALERT_WORKFLOW_STATUS]: 'open',
|
||||
[SPACE_IDS]: ['default'],
|
||||
[VERSION]: fullSignal[VERSION],
|
||||
[VERSION]: fullAlert[VERSION],
|
||||
threat: {
|
||||
enrichments: get(fullSignal, 'threat.enrichments'),
|
||||
enrichments: get(fullAlert, 'threat.enrichments'),
|
||||
},
|
||||
...flattenWithPrefix(ALERT_RULE_NAMESPACE, {
|
||||
actions: [],
|
||||
author: [],
|
||||
category: 'Indicator Match Rule',
|
||||
consumer: 'siem',
|
||||
created_by: 'elastic',
|
||||
created_by: ELASTICSEARCH_USERNAME,
|
||||
description: 'Detecting root and admin users',
|
||||
enabled: true,
|
||||
exceptions_list: [],
|
||||
|
@ -500,18 +504,18 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
threat: [],
|
||||
to: 'now',
|
||||
type: 'threat_match',
|
||||
updated_at: fullSignal[ALERT_RULE_UPDATED_AT],
|
||||
updated_by: 'elastic',
|
||||
uuid: fullSignal[ALERT_RULE_UUID],
|
||||
updated_at: fullAlert[ALERT_RULE_UPDATED_AT],
|
||||
updated_by: ELASTICSEARCH_USERNAME,
|
||||
uuid: fullAlert[ALERT_RULE_UUID],
|
||||
version: 1,
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
it('generates max signals warning when circuit breaker is hit', async () => {
|
||||
it('generates max alerts warning when circuit breaker is hit', async () => {
|
||||
const rule: ThreatMatchRuleCreateProps = { ...createThreatMatchRule(), max_signals: 87 }; // Query generates 88 alerts with current esArchive
|
||||
const { logs } = await previewRule({ supertest, rule });
|
||||
expect(logs[0].warnings).contain(getMaxSignalsWarning());
|
||||
expect(logs[0].warnings).contain(getMaxAlertsWarning());
|
||||
});
|
||||
|
||||
it('terms and match should have the same alerts with pagination', async () => {
|
||||
|
@ -552,7 +556,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
|
||||
const createdRuleTerm = await createRule(supertest, log, termRule);
|
||||
const createdRuleMatch = await createRule(supertest, log, matchRule);
|
||||
const alertsTerm = await getOpenSignals(
|
||||
const alertsTerm = await getOpenAlerts(
|
||||
supertest,
|
||||
log,
|
||||
es,
|
||||
|
@ -560,7 +564,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
RuleExecutionStatusEnum.succeeded,
|
||||
100
|
||||
);
|
||||
const alertsMatch = await getOpenSignals(
|
||||
const alertsMatch = await getOpenAlerts(
|
||||
supertest,
|
||||
log,
|
||||
es,
|
||||
|
@ -593,7 +597,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
expect(previewAlerts.length).equal(0);
|
||||
});
|
||||
|
||||
it('should return 0 signals when using an AND and one of the clauses does not have data', async () => {
|
||||
it('should return 0 alerts when using an AND and one of the clauses does not have data', async () => {
|
||||
const rule: ThreatMatchRuleCreateProps = createThreatMatchRule({
|
||||
threat_mapping: [
|
||||
{
|
||||
|
@ -618,7 +622,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
expect(previewAlerts.length).equal(0);
|
||||
});
|
||||
|
||||
it('should return 0 signals when using an AND and one of the clauses has a made up value that does not exist', async () => {
|
||||
it('should return 0 alerts when using an AND and one of the clauses has a made up value that does not exist', async () => {
|
||||
const rule: ThreatMatchRuleCreateProps = createThreatMatchRule({
|
||||
threat_mapping: [
|
||||
{
|
||||
|
@ -661,14 +665,14 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
|
||||
describe('indicator enrichment: threat-first search', () => {
|
||||
before(async () => {
|
||||
await esArchiver.load('x-pack/test/functional/es_archives/filebeat/threat_intel');
|
||||
await esArchiver.load(threatIntelPath);
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await esArchiver.unload('x-pack/test/functional/es_archives/filebeat/threat_intel');
|
||||
await esArchiver.unload(threatIntelPath);
|
||||
});
|
||||
|
||||
it('enriches signals with the single indicator that matched', async () => {
|
||||
it('enriches alerts with the single indicator that matched', async () => {
|
||||
const rule: ThreatMatchRuleCreateProps = createThreatMatchRule({
|
||||
threat_mapping: [
|
||||
{
|
||||
|
@ -744,9 +748,9 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
]);
|
||||
});
|
||||
|
||||
it('enriches signals with multiple indicators if several matched', async () => {
|
||||
it('enriches alerts with multiple indicators if several matched', async () => {
|
||||
const rule: ThreatMatchRuleCreateProps = createThreatMatchRule({
|
||||
query: 'NOT source.port:35326', // specify query to have signals more than treat indicators, but only 1 will match
|
||||
query: 'NOT source.port:35326', // specify query to have alerts more than treat indicators, but only 1 will match
|
||||
threat_query: 'threat.indicator.ip: *',
|
||||
threat_index: ['filebeat-*'], // Mimics indicators from the filebeat MISP module
|
||||
threat_mapping: [
|
||||
|
@ -811,7 +815,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
|
||||
it('adds a single indicator that matched multiple fields', async () => {
|
||||
const rule: ThreatMatchRuleCreateProps = createThreatMatchRule({
|
||||
query: 'NOT source.port:35326', // specify query to have signals more than treat indicators, but only 1 will match
|
||||
query: 'NOT source.port:35326', // specify query to have alerts more than treat indicators, but only 1 will match
|
||||
threat_query: 'threat.indicator.port: 57324 or threat.indicator.ip:45.115.45.3', // narrow our query to a single indicator
|
||||
threat_index: ['filebeat-*'], // Mimics indicators from the filebeat MISP module
|
||||
threat_mapping: [
|
||||
|
@ -907,7 +911,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
]);
|
||||
});
|
||||
|
||||
it('generates multiple signals with multiple matches', async () => {
|
||||
it('generates multiple alerts with multiple matches', async () => {
|
||||
const rule: ThreatMatchRuleCreateProps = createThreatMatchRule({
|
||||
threat_query: '*:*',
|
||||
threat_index: ['filebeat-*'], // Mimics indicators from the filebeat MISP module
|
||||
|
@ -1032,7 +1036,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
});
|
||||
|
||||
// https://github.com/elastic/kibana/issues/149920
|
||||
// generates same number of alerts similarly to "enriches signals with the single indicator that matches" test
|
||||
// generates same number of alerts similarly to "enriches alerts with the single indicator that matches" test
|
||||
it('generates alerts with single match if queries contain field path wildcards', async () => {
|
||||
const rule: ThreatMatchRuleCreateProps = createThreatMatchRule({
|
||||
// still matches all documents as default *:*
|
||||
|
@ -1060,14 +1064,14 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
|
||||
describe('indicator enrichment: event-first search', () => {
|
||||
before(async () => {
|
||||
await esArchiver.load('x-pack/test/functional/es_archives/filebeat/threat_intel');
|
||||
await esArchiver.load(threatIntelPath);
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await esArchiver.unload('x-pack/test/functional/es_archives/filebeat/threat_intel');
|
||||
await esArchiver.unload(threatIntelPath);
|
||||
});
|
||||
|
||||
it('enriches signals with the single indicator that matched', async () => {
|
||||
it('enriches alerts with the single indicator that matched', async () => {
|
||||
const termRule: ThreatMatchRuleCreateProps = createThreatMatchRule({
|
||||
query: 'destination.ip:159.89.119.67',
|
||||
threat_query: 'threat.indicator.domain: *', // narrow things down to indicators with a domain
|
||||
|
@ -1168,7 +1172,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
alertsAreTheSame(termPreviewAlerts, matchPrevieAlerts);
|
||||
});
|
||||
|
||||
it('enriches signals with multiple indicators if several matched', async () => {
|
||||
it('enriches alerts with multiple indicators if several matched', async () => {
|
||||
const termRule: ThreatMatchRuleCreateProps = createThreatMatchRule({
|
||||
query: 'source.port: 57324', // narrow our query to a single record that matches two indicatorsthreat_query: 'threat.indicator.ip: *',
|
||||
threat_query: 'threat.indicator.ip: *',
|
||||
|
@ -1408,7 +1412,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
);
|
||||
});
|
||||
|
||||
it('generates multiple signals with multiple matches', async () => {
|
||||
it('generates multiple alerts with multiple matches', async () => {
|
||||
const rule: ThreatMatchRuleCreateProps = createThreatMatchRule({
|
||||
query: '(source.port:57324 and source.ip:45.115.45.3) or destination.ip:159.89.119.67', // narrow our query to a single record that matches two indicators
|
||||
threat_query: '*:*',
|
||||
|
@ -1534,7 +1538,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
});
|
||||
|
||||
// https://github.com/elastic/kibana/issues/149920
|
||||
// creates same number of alerts similarly to "generates multiple signals with multiple matches" test
|
||||
// creates same number of alerts similarly to "generates multiple alerts with multiple matches" test
|
||||
it('generates alerts with multiple matches if queries contain field path wildcards', async () => {
|
||||
const rule: ThreatMatchRuleCreateProps = createThreatMatchRule({
|
||||
// source.po* matches port source.port field
|
||||
|
@ -1605,16 +1609,16 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
const previewAlerts = await getPreviewAlerts({ es, previewId, size: 100 });
|
||||
expect(previewAlerts.length).equal(88);
|
||||
const fullSource = previewAlerts.find(
|
||||
(signal) =>
|
||||
(signal._source?.[ALERT_ANCESTORS] as Ancestor[])[0].id === '7yJ-B2kBR346wHgnhlMn'
|
||||
(alert) =>
|
||||
(alert._source?.[ALERT_ANCESTORS] as Ancestor[])[0].id === '7yJ-B2kBR346wHgnhlMn'
|
||||
);
|
||||
const fullSignal = fullSource?._source;
|
||||
if (!fullSignal) {
|
||||
return expect(fullSignal).to.be.ok();
|
||||
const fullAlert = fullSource?._source;
|
||||
if (!fullAlert) {
|
||||
return expect(fullAlert).to.be.ok();
|
||||
}
|
||||
|
||||
expect(fullSignal?.host?.risk?.calculated_level).to.eql('Critical');
|
||||
expect(fullSignal?.host?.risk?.calculated_score_norm).to.eql(70);
|
||||
expect(fullAlert?.host?.risk?.calculated_level).to.eql('Critical');
|
||||
expect(fullAlert?.host?.risk?.calculated_score_norm).to.eql(70);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -21,51 +21,56 @@ import {
|
|||
ALERT_ORIGINAL_TIME,
|
||||
ALERT_THRESHOLD_RESULT,
|
||||
} from '@kbn/security-solution-plugin/common/field_maps/field_names';
|
||||
import { getMaxSignalsWarning } from '@kbn/security-solution-plugin/server/lib/detection_engine/rule_types/utils/utils';
|
||||
import { getMaxSignalsWarning as getMaxAlertsWarning } from '@kbn/security-solution-plugin/server/lib/detection_engine/rule_types/utils/utils';
|
||||
import {
|
||||
createRule,
|
||||
getOpenSignals,
|
||||
getOpenAlerts,
|
||||
getPreviewAlerts,
|
||||
getThresholdRuleForSignalTesting,
|
||||
getThresholdRuleForAlertTesting,
|
||||
previewRule,
|
||||
} from '../../utils';
|
||||
import { FtrProviderContext } from '../../common/ftr_provider_context';
|
||||
} from '../../../utils';
|
||||
import { FtrProviderContext } from '../../../../../ftr_provider_context';
|
||||
import { EsArchivePathBuilder } from '../../../../../es_archive_path_builder';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default ({ getService }: FtrProviderContext) => {
|
||||
const supertest = getService('supertest');
|
||||
const esArchiver = getService('esArchiver');
|
||||
const es = getService('es');
|
||||
const log = getService('log');
|
||||
// TODO: add a new service
|
||||
const config = getService('config');
|
||||
const isServerless = config.get('serverless');
|
||||
const dataPathBuilder = new EsArchivePathBuilder(isServerless);
|
||||
const path = dataPathBuilder.getPath('auditbeat/hosts');
|
||||
|
||||
describe('Threshold type rules', () => {
|
||||
describe('@ess @serverless Threshold type rules', () => {
|
||||
before(async () => {
|
||||
await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/hosts');
|
||||
await esArchiver.load(path);
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/hosts');
|
||||
await esArchiver.unload(path);
|
||||
});
|
||||
|
||||
// First test creates a real rule - remaining tests use preview API
|
||||
it('generates 1 signal from Threshold rules when threshold is met', async () => {
|
||||
it('generates 1 alert from Threshold rules when threshold is met', async () => {
|
||||
const rule: ThresholdRuleCreateProps = {
|
||||
...getThresholdRuleForSignalTesting(['auditbeat-*']),
|
||||
...getThresholdRuleForAlertTesting(['auditbeat-*']),
|
||||
threshold: {
|
||||
field: ['host.id'],
|
||||
value: 700,
|
||||
},
|
||||
};
|
||||
const createdRule = await createRule(supertest, log, rule);
|
||||
const alerts = await getOpenSignals(supertest, log, es, createdRule);
|
||||
const alerts = await getOpenAlerts(supertest, log, es, createdRule);
|
||||
expect(alerts.hits.hits.length).eql(1);
|
||||
const fullSignal = alerts.hits.hits[0]._source;
|
||||
if (!fullSignal) {
|
||||
return expect(fullSignal).to.be.ok();
|
||||
const fullAlert = alerts.hits.hits[0]._source;
|
||||
if (!fullAlert) {
|
||||
return expect(fullAlert).to.be.ok();
|
||||
}
|
||||
const eventIds = (fullSignal?.[ALERT_ANCESTORS] as Ancestor[]).map((event) => event.id);
|
||||
expect(fullSignal).eql({
|
||||
...fullSignal,
|
||||
const eventIds = (fullAlert?.[ALERT_ANCESTORS] as Ancestor[]).map((event) => event.id);
|
||||
expect(fullAlert).eql({
|
||||
...fullAlert,
|
||||
'host.id': '8cc95778cce5407c809480e8e32ad76b',
|
||||
[EVENT_KIND]: 'signal',
|
||||
[ALERT_ANCESTORS]: [
|
||||
|
@ -77,9 +82,9 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
},
|
||||
],
|
||||
[ALERT_WORKFLOW_STATUS]: 'open',
|
||||
[ALERT_REASON]: 'event created high alert Signal Testing Query.',
|
||||
[ALERT_RULE_UUID]: fullSignal[ALERT_RULE_UUID],
|
||||
[ALERT_ORIGINAL_TIME]: fullSignal[ALERT_ORIGINAL_TIME],
|
||||
[ALERT_REASON]: 'event created high alert Alert Testing Query.',
|
||||
[ALERT_RULE_UUID]: fullAlert[ALERT_RULE_UUID],
|
||||
[ALERT_ORIGINAL_TIME]: fullAlert[ALERT_ORIGINAL_TIME],
|
||||
[ALERT_DEPTH]: 1,
|
||||
[ALERT_THRESHOLD_RESULT]: {
|
||||
terms: [
|
||||
|
@ -94,9 +99,9 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
});
|
||||
});
|
||||
|
||||
it('generates max signals warning when circuit breaker is exceeded', async () => {
|
||||
it('generates max alerts warning when circuit breaker is exceeded', async () => {
|
||||
const rule: ThresholdRuleCreateProps = {
|
||||
...getThresholdRuleForSignalTesting(['auditbeat-*']),
|
||||
...getThresholdRuleForAlertTesting(['auditbeat-*']),
|
||||
threshold: {
|
||||
field: 'host.id',
|
||||
value: 1, // This value generates 7 alerts with the current esArchive
|
||||
|
@ -104,12 +109,12 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
max_signals: 5,
|
||||
};
|
||||
const { logs } = await previewRule({ supertest, rule });
|
||||
expect(logs[0].warnings).contain(getMaxSignalsWarning());
|
||||
expect(logs[0].warnings).contain(getMaxAlertsWarning());
|
||||
});
|
||||
|
||||
it("doesn't generate max signals warning when circuit breaker is met but not exceeded", async () => {
|
||||
it("doesn't generate max alerts warning when circuit breaker is met but not exceeded", async () => {
|
||||
const rule: ThresholdRuleCreateProps = {
|
||||
...getThresholdRuleForSignalTesting(['auditbeat-*']),
|
||||
...getThresholdRuleForAlertTesting(['auditbeat-*']),
|
||||
threshold: {
|
||||
field: 'host.id',
|
||||
value: 1, // This value generates 7 alerts with the current esArchive
|
||||
|
@ -117,12 +122,12 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
max_signals: 7,
|
||||
};
|
||||
const { logs } = await previewRule({ supertest, rule });
|
||||
expect(logs[0].warnings).not.contain(getMaxSignalsWarning());
|
||||
expect(logs[0].warnings).not.contain(getMaxAlertsWarning());
|
||||
});
|
||||
|
||||
it('generates 2 signals from Threshold rules when threshold is met', async () => {
|
||||
it('generates 2 alerts from Threshold rules when threshold is met', async () => {
|
||||
const rule: ThresholdRuleCreateProps = {
|
||||
...getThresholdRuleForSignalTesting(['auditbeat-*']),
|
||||
...getThresholdRuleForAlertTesting(['auditbeat-*']),
|
||||
threshold: {
|
||||
field: 'host.id',
|
||||
value: 100,
|
||||
|
@ -135,7 +140,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
|
||||
it('applies the provided query before bucketing ', async () => {
|
||||
const rule: ThresholdRuleCreateProps = {
|
||||
...getThresholdRuleForSignalTesting(['auditbeat-*']),
|
||||
...getThresholdRuleForAlertTesting(['auditbeat-*']),
|
||||
query: 'host.id:"2ab45fc1c41e4c84bbd02202a7e5761f"',
|
||||
threshold: {
|
||||
field: 'process.name',
|
||||
|
@ -147,9 +152,9 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
expect(previewAlerts.length).eql(1);
|
||||
});
|
||||
|
||||
it('generates no signals from Threshold rules when threshold is met and cardinality is not met', async () => {
|
||||
it('generates no alerts from Threshold rules when threshold is met and cardinality is not met', async () => {
|
||||
const rule: ThresholdRuleCreateProps = {
|
||||
...getThresholdRuleForSignalTesting(['auditbeat-*']),
|
||||
...getThresholdRuleForAlertTesting(['auditbeat-*']),
|
||||
threshold: {
|
||||
field: 'host.id',
|
||||
value: 100,
|
||||
|
@ -166,9 +171,9 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
expect(previewAlerts.length).eql(0);
|
||||
});
|
||||
|
||||
it('generates no signals from Threshold rules when cardinality is met and threshold is not met', async () => {
|
||||
it('generates no alerts from Threshold rules when cardinality is met and threshold is not met', async () => {
|
||||
const rule: ThresholdRuleCreateProps = {
|
||||
...getThresholdRuleForSignalTesting(['auditbeat-*']),
|
||||
...getThresholdRuleForAlertTesting(['auditbeat-*']),
|
||||
threshold: {
|
||||
field: 'host.id',
|
||||
value: 1000,
|
||||
|
@ -185,9 +190,9 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
expect(previewAlerts.length).eql(0);
|
||||
});
|
||||
|
||||
it('generates signals from Threshold rules when threshold and cardinality are both met', async () => {
|
||||
it('generates alerts from Threshold rules when threshold and cardinality are both met', async () => {
|
||||
const rule: ThresholdRuleCreateProps = {
|
||||
...getThresholdRuleForSignalTesting(['auditbeat-*']),
|
||||
...getThresholdRuleForAlertTesting(['auditbeat-*']),
|
||||
threshold: {
|
||||
field: 'host.id',
|
||||
value: 100,
|
||||
|
@ -202,13 +207,13 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
const { previewId } = await previewRule({ supertest, rule });
|
||||
const previewAlerts = await getPreviewAlerts({ es, previewId });
|
||||
expect(previewAlerts.length).eql(1);
|
||||
const fullSignal = previewAlerts[0]._source;
|
||||
if (!fullSignal) {
|
||||
return expect(fullSignal).to.be.ok();
|
||||
const fullAlert = previewAlerts[0]._source;
|
||||
if (!fullAlert) {
|
||||
return expect(fullAlert).to.be.ok();
|
||||
}
|
||||
const eventIds = (fullSignal?.[ALERT_ANCESTORS] as Ancestor[]).map((event) => event.id);
|
||||
expect(fullSignal).eql({
|
||||
...fullSignal,
|
||||
const eventIds = (fullAlert?.[ALERT_ANCESTORS] as Ancestor[]).map((event) => event.id);
|
||||
expect(fullAlert).eql({
|
||||
...fullAlert,
|
||||
'host.id': '8cc95778cce5407c809480e8e32ad76b',
|
||||
[EVENT_KIND]: 'signal',
|
||||
[ALERT_ANCESTORS]: [
|
||||
|
@ -220,9 +225,9 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
},
|
||||
],
|
||||
[ALERT_WORKFLOW_STATUS]: 'open',
|
||||
[ALERT_REASON]: `event created high alert Signal Testing Query.`,
|
||||
[ALERT_RULE_UUID]: fullSignal[ALERT_RULE_UUID],
|
||||
[ALERT_ORIGINAL_TIME]: fullSignal[ALERT_ORIGINAL_TIME],
|
||||
[ALERT_REASON]: `event created high alert Alert Testing Query.`,
|
||||
[ALERT_RULE_UUID]: fullAlert[ALERT_RULE_UUID],
|
||||
[ALERT_ORIGINAL_TIME]: fullAlert[ALERT_ORIGINAL_TIME],
|
||||
[ALERT_DEPTH]: 1,
|
||||
[ALERT_THRESHOLD_RESULT]: {
|
||||
terms: [
|
||||
|
@ -243,9 +248,9 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should not generate signals if only one field meets the threshold requirement', async () => {
|
||||
it('should not generate alerts if only one field meets the threshold requirement', async () => {
|
||||
const rule: ThresholdRuleCreateProps = {
|
||||
...getThresholdRuleForSignalTesting(['auditbeat-*']),
|
||||
...getThresholdRuleForAlertTesting(['auditbeat-*']),
|
||||
threshold: {
|
||||
field: ['host.id', 'process.name'],
|
||||
value: 22,
|
||||
|
@ -256,9 +261,9 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
expect(previewAlerts.length).eql(0);
|
||||
});
|
||||
|
||||
it('generates signals from Threshold rules when bucketing by multiple fields', async () => {
|
||||
it('generates alerts from Threshold rules when bucketing by multiple fields', async () => {
|
||||
const rule: ThresholdRuleCreateProps = {
|
||||
...getThresholdRuleForSignalTesting(['auditbeat-*']),
|
||||
...getThresholdRuleForAlertTesting(['auditbeat-*']),
|
||||
threshold: {
|
||||
field: ['host.id', 'process.name', 'event.module'],
|
||||
value: 21,
|
||||
|
@ -267,13 +272,13 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
const { previewId } = await previewRule({ supertest, rule });
|
||||
const previewAlerts = await getPreviewAlerts({ es, previewId });
|
||||
expect(previewAlerts.length).eql(1);
|
||||
const fullSignal = previewAlerts[0]._source;
|
||||
if (!fullSignal) {
|
||||
return expect(fullSignal).to.be.ok();
|
||||
const fullAlert = previewAlerts[0]._source;
|
||||
if (!fullAlert) {
|
||||
return expect(fullAlert).to.be.ok();
|
||||
}
|
||||
const eventIds = (fullSignal[ALERT_ANCESTORS] as Ancestor[]).map((event) => event.id);
|
||||
expect(fullSignal).eql({
|
||||
...fullSignal,
|
||||
const eventIds = (fullAlert[ALERT_ANCESTORS] as Ancestor[]).map((event) => event.id);
|
||||
expect(fullAlert).eql({
|
||||
...fullAlert,
|
||||
'event.module': 'system',
|
||||
'host.id': '2ab45fc1c41e4c84bbd02202a7e5761f',
|
||||
'process.name': 'sshd',
|
||||
|
@ -287,9 +292,9 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
},
|
||||
],
|
||||
[ALERT_WORKFLOW_STATUS]: 'open',
|
||||
[ALERT_REASON]: `event with process sshd, created high alert Signal Testing Query.`,
|
||||
[ALERT_RULE_UUID]: fullSignal[ALERT_RULE_UUID],
|
||||
[ALERT_ORIGINAL_TIME]: fullSignal[ALERT_ORIGINAL_TIME],
|
||||
[ALERT_REASON]: `event with process sshd, created high alert Alert Testing Query.`,
|
||||
[ALERT_RULE_UUID]: fullAlert[ALERT_RULE_UUID],
|
||||
[ALERT_ORIGINAL_TIME]: fullAlert[ALERT_ORIGINAL_TIME],
|
||||
[ALERT_DEPTH]: 1,
|
||||
[ALERT_THRESHOLD_RESULT]: {
|
||||
terms: [
|
||||
|
@ -315,15 +320,15 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
// https://github.com/elastic/kibana/issues/149920
|
||||
it('generates 1 alert when threshold is met and rule query has wildcard in field name', async () => {
|
||||
const rule: ThresholdRuleCreateProps = {
|
||||
...getThresholdRuleForSignalTesting(['auditbeat-*']),
|
||||
query: 'agent.ty*:auditbeat', // this query should match all documents from index and we will receive 1 alert, similarly to "generates 1 signal from Threshold rules when threshold is met" test case
|
||||
...getThresholdRuleForAlertTesting(['auditbeat-*']),
|
||||
query: 'agent.ty*:auditbeat', // this query should match all documents from index and we will receive 1 alert, similarly to "generates 1 alert from Threshold rules when threshold is met" test case
|
||||
threshold: {
|
||||
field: ['host.id'],
|
||||
value: 700,
|
||||
},
|
||||
};
|
||||
const createdRule = await createRule(supertest, log, rule);
|
||||
const alerts = await getOpenSignals(supertest, log, es, createdRule);
|
||||
const alerts = await getOpenAlerts(supertest, log, es, createdRule);
|
||||
expect(alerts.hits.hits.length).eql(1);
|
||||
});
|
||||
|
||||
|
@ -342,7 +347,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
|
||||
it('applies timestamp override when using single field', async () => {
|
||||
const rule: ThresholdRuleCreateProps = {
|
||||
...getThresholdRuleForSignalTesting(['timestamp-fallback-test']),
|
||||
...getThresholdRuleForAlertTesting(['timestamp-fallback-test']),
|
||||
threshold: {
|
||||
field: 'host.name',
|
||||
value: 1,
|
||||
|
@ -370,7 +375,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
|
||||
it('applies timestamp override when using multiple fields', async () => {
|
||||
const rule: ThresholdRuleCreateProps = {
|
||||
...getThresholdRuleForSignalTesting(['timestamp-fallback-test']),
|
||||
...getThresholdRuleForAlertTesting(['timestamp-fallback-test']),
|
||||
threshold: {
|
||||
field: ['host.name', 'source.ip'],
|
||||
value: 1,
|
||||
|
@ -408,7 +413,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
|
||||
it('should be enriched with host risk score', async () => {
|
||||
const rule: ThresholdRuleCreateProps = {
|
||||
...getThresholdRuleForSignalTesting(['auditbeat-*']),
|
||||
...getThresholdRuleForAlertTesting(['auditbeat-*']),
|
||||
threshold: {
|
||||
field: 'host.name',
|
||||
value: 100,
|
|
@ -7,16 +7,15 @@
|
|||
|
||||
import expect from '@kbn/expect';
|
||||
|
||||
import { FtrProviderContext } from '../../../common/ftr_provider_context';
|
||||
import {
|
||||
createRule,
|
||||
createSignalsIndex,
|
||||
createAlertsIndex,
|
||||
deleteAllRules,
|
||||
deleteAllAlerts,
|
||||
getEqlRuleForSignalTesting,
|
||||
getSignalsById,
|
||||
getEqlRuleForAlertTesting,
|
||||
getAlertsById,
|
||||
waitForRuleSuccess,
|
||||
waitForSignalsToBePresent,
|
||||
waitForAlertsToBePresent,
|
||||
} from '../../utils';
|
||||
|
||||
interface Ignore {
|
||||
|
@ -26,10 +25,10 @@ interface Ignore {
|
|||
testing_regex?: string;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
import { FtrProviderContext } from '../../../../ftr_provider_context';
|
||||
export default ({ getService }: FtrProviderContext): void => {
|
||||
/**
|
||||
* See the config file (detection_engine_api_integration/common/config.ts) for which field values were added to be ignored
|
||||
* See the config file (config.ts) for which field values were added to be ignored
|
||||
* for testing. The values should be in the config around the area of:
|
||||
* --xpack.securitySolution.alertIgnoreFields=[testing.ignore_1,/[testingRegex]
|
||||
* meaning that the ignore fields values should be the array: ["testing.ignore_1", "/[testingRegex]/"]
|
||||
|
@ -47,7 +46,7 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
* server/lib/detection_engine/signals/source_fields_merging/utils/is_ignored.ts
|
||||
* server/lib/detection_engine/signals/source_fields_merging/utils/is_eql_bug_77152.ts
|
||||
*/
|
||||
describe('ignore_fields', () => {
|
||||
describe('@ess @serverless ignore_fields', () => {
|
||||
const supertest = getService('supertest');
|
||||
const esArchiver = getService('esArchiver');
|
||||
const log = getService('log');
|
||||
|
@ -62,7 +61,7 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await createSignalsIndex(supertest, log);
|
||||
await createAlertsIndex(supertest, log);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
|
@ -70,14 +69,14 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
await deleteAllRules(supertest, log);
|
||||
});
|
||||
|
||||
it('should ignore the field of "testing_ignored"', async () => {
|
||||
const rule = getEqlRuleForSignalTesting(['ignore_fields']);
|
||||
it('@skipInQA should ignore the field of "testing_ignored"', async () => {
|
||||
const rule = getEqlRuleForAlertTesting(['ignore_fields']);
|
||||
|
||||
const { id } = await createRule(supertest, log, rule);
|
||||
await waitForRuleSuccess({ supertest, log, id });
|
||||
await waitForSignalsToBePresent(supertest, log, 4, [id]);
|
||||
const signalsOpen = await getSignalsById(supertest, log, id);
|
||||
const hits = signalsOpen.hits.hits
|
||||
await waitForAlertsToBePresent(supertest, log, 4, [id]);
|
||||
const alertsOpen = await getAlertsById(supertest, log, id);
|
||||
const hits = alertsOpen.hits.hits
|
||||
.map((hit) => (hit._source as Ignore).testing_ignored)
|
||||
.sort();
|
||||
|
||||
|
@ -85,27 +84,27 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
expect(hits).to.eql([undefined, undefined, undefined, undefined]);
|
||||
});
|
||||
|
||||
it('should ignore the field of "testing_regex"', async () => {
|
||||
const rule = getEqlRuleForSignalTesting(['ignore_fields']);
|
||||
it('@skipInQA should ignore the field of "testing_regex"', async () => {
|
||||
const rule = getEqlRuleForAlertTesting(['ignore_fields']);
|
||||
|
||||
const { id } = await createRule(supertest, log, rule);
|
||||
await waitForRuleSuccess({ supertest, log, id });
|
||||
await waitForSignalsToBePresent(supertest, log, 4, [id]);
|
||||
const signalsOpen = await getSignalsById(supertest, log, id);
|
||||
const hits = signalsOpen.hits.hits.map((hit) => (hit._source as Ignore).testing_regex).sort();
|
||||
await waitForAlertsToBePresent(supertest, log, 4, [id]);
|
||||
const alertsOpen = await getAlertsById(supertest, log, id);
|
||||
const hits = alertsOpen.hits.hits.map((hit) => (hit._source as Ignore).testing_regex).sort();
|
||||
|
||||
// Value should be "undefined for all records"
|
||||
expect(hits).to.eql([undefined, undefined, undefined, undefined]);
|
||||
});
|
||||
|
||||
it('should have the field of "normal_constant"', async () => {
|
||||
const rule = getEqlRuleForSignalTesting(['ignore_fields']);
|
||||
const rule = getEqlRuleForAlertTesting(['ignore_fields']);
|
||||
|
||||
const { id } = await createRule(supertest, log, rule);
|
||||
await waitForRuleSuccess({ supertest, log, id });
|
||||
await waitForSignalsToBePresent(supertest, log, 4, [id]);
|
||||
const signalsOpen = await getSignalsById(supertest, log, id);
|
||||
const hits = signalsOpen.hits.hits
|
||||
await waitForAlertsToBePresent(supertest, log, 4, [id]);
|
||||
const alertsOpen = await getAlertsById(supertest, log, id);
|
||||
const hits = alertsOpen.hits.hits
|
||||
.map((hit) => (hit._source as Ignore).normal_constant)
|
||||
.sort();
|
||||
|
||||
|
@ -115,13 +114,13 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
|
||||
// TODO: Remove this test once https://github.com/elastic/elasticsearch/issues/77152 is fixed
|
||||
it('should ignore the field of "_ignored" when using EQL and index the data', async () => {
|
||||
const rule = getEqlRuleForSignalTesting(['ignore_fields']);
|
||||
const rule = getEqlRuleForAlertTesting(['ignore_fields']);
|
||||
|
||||
const { id } = await createRule(supertest, log, rule);
|
||||
await waitForRuleSuccess({ supertest, log, id });
|
||||
await waitForSignalsToBePresent(supertest, log, 4, [id]);
|
||||
const signalsOpen = await getSignalsById(supertest, log, id);
|
||||
const hits = signalsOpen.hits.hits.map((hit) => (hit._source as Ignore).small_field).sort();
|
||||
await waitForAlertsToBePresent(supertest, log, 4, [id]);
|
||||
const alertsOpen = await getAlertsById(supertest, log, id);
|
||||
const hits = alertsOpen.hits.hits.map((hit) => (hit._source as Ignore).small_field).sort();
|
||||
|
||||
// We just test a constant value to ensure this did not blow up on us and did index data.
|
||||
expect(hits).to.eql([
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { FtrProviderContext } from '../../../../ftr_provider_context';
|
||||
|
||||
export default function ({ loadTestFile }: FtrProviderContext) {
|
||||
describe('Rule execution logic API', function () {
|
||||
loadTestFile(require.resolve('./keyword_family'));
|
||||
loadTestFile(require.resolve('./ignore_fields'));
|
||||
loadTestFile(require.resolve('./runtime'));
|
||||
loadTestFile(require.resolve('./execution_logic'));
|
||||
loadTestFile(require.resolve('./timestamps'));
|
||||
});
|
||||
}
|
|
@ -12,28 +12,27 @@ import {
|
|||
} from '@kbn/security-solution-plugin/common/api/detection_engine';
|
||||
import { ALERT_THRESHOLD_RESULT } from '@kbn/security-solution-plugin/common/field_maps/field_names';
|
||||
|
||||
import { FtrProviderContext } from '../../../common/ftr_provider_context';
|
||||
import {
|
||||
createRule,
|
||||
createSignalsIndex,
|
||||
createAlertsIndex,
|
||||
deleteAllRules,
|
||||
deleteAllAlerts,
|
||||
getEqlRuleForSignalTesting,
|
||||
getRuleForSignalTesting,
|
||||
getSignalsById,
|
||||
getThresholdRuleForSignalTesting,
|
||||
getEqlRuleForAlertTesting,
|
||||
getRuleForAlertTesting,
|
||||
getAlertsById,
|
||||
getThresholdRuleForAlertTesting,
|
||||
waitForRuleSuccess,
|
||||
waitForSignalsToBePresent,
|
||||
waitForAlertsToBePresent,
|
||||
} from '../../../utils';
|
||||
import { FtrProviderContext } from '../../../../../ftr_provider_context';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default ({ getService }: FtrProviderContext) => {
|
||||
const supertest = getService('supertest');
|
||||
const esArchiver = getService('esArchiver');
|
||||
const log = getService('log');
|
||||
const es = getService('es');
|
||||
|
||||
describe('Rule detects against a keyword of event.dataset', () => {
|
||||
describe('@ess @serverless Rule detects against a keyword of event.dataset', () => {
|
||||
before(async () => {
|
||||
await esArchiver.load('x-pack/test/functional/es_archives/rule_keyword_family/const_keyword');
|
||||
});
|
||||
|
@ -45,7 +44,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await createSignalsIndex(supertest, log);
|
||||
await createAlertsIndex(supertest, log);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
|
@ -54,28 +53,28 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
});
|
||||
|
||||
describe('"kql" rule type', () => {
|
||||
it('should detect the "dataset_name_1" from "event.dataset" and have 4 signals', async () => {
|
||||
it('should detect the "dataset_name_1" from "event.dataset" and have 4 alerts', async () => {
|
||||
const rule = {
|
||||
...getRuleForSignalTesting(['const_keyword']),
|
||||
...getRuleForAlertTesting(['const_keyword']),
|
||||
query: 'event.dataset: "dataset_name_1"',
|
||||
};
|
||||
const { id } = await createRule(supertest, log, rule);
|
||||
await waitForRuleSuccess({ supertest, log, id });
|
||||
await waitForSignalsToBePresent(supertest, log, 4, [id]);
|
||||
const signalsOpen = await getSignalsById(supertest, log, id);
|
||||
expect(signalsOpen.hits.hits.length).to.eql(4);
|
||||
await waitForAlertsToBePresent(supertest, log, 4, [id]);
|
||||
const alertsOpen = await getAlertsById(supertest, log, id);
|
||||
expect(alertsOpen.hits.hits.length).to.eql(4);
|
||||
});
|
||||
|
||||
it('should copy the dataset_name_1 from the index into the signal', async () => {
|
||||
it('should copy the dataset_name_1 from the index into the alert', async () => {
|
||||
const rule = {
|
||||
...getRuleForSignalTesting(['const_keyword']),
|
||||
...getRuleForAlertTesting(['const_keyword']),
|
||||
query: 'event.dataset: "dataset_name_1"',
|
||||
};
|
||||
const { id } = await createRule(supertest, log, rule);
|
||||
await waitForRuleSuccess({ supertest, log, id });
|
||||
await waitForSignalsToBePresent(supertest, log, 4, [id]);
|
||||
const signalsOpen = await getSignalsById(supertest, log, id);
|
||||
const hits = signalsOpen.hits.hits.map((hit) => hit._source?.['event.dataset']).sort();
|
||||
await waitForAlertsToBePresent(supertest, log, 4, [id]);
|
||||
const alertsOpen = await getAlertsById(supertest, log, id);
|
||||
const hits = alertsOpen.hits.hits.map((hit) => hit._source?.['event.dataset']).sort();
|
||||
expect(hits).to.eql([
|
||||
'dataset_name_1',
|
||||
'dataset_name_1',
|
||||
|
@ -86,30 +85,30 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
});
|
||||
|
||||
describe('"eql" rule type', () => {
|
||||
it('should detect the "dataset_name_1" from "event.dataset" and have 4 signals', async () => {
|
||||
it('should detect the "dataset_name_1" from "event.dataset" and have 4 alerts', async () => {
|
||||
const rule: EqlRuleCreateProps = {
|
||||
...getEqlRuleForSignalTesting(['const_keyword']),
|
||||
...getEqlRuleForAlertTesting(['const_keyword']),
|
||||
query: 'any where event.dataset=="dataset_name_1"',
|
||||
};
|
||||
|
||||
const { id } = await createRule(supertest, log, rule);
|
||||
await waitForRuleSuccess({ supertest, log, id });
|
||||
await waitForSignalsToBePresent(supertest, log, 4, [id]);
|
||||
const signalsOpen = await getSignalsById(supertest, log, id);
|
||||
expect(signalsOpen.hits.hits.length).to.eql(4);
|
||||
await waitForAlertsToBePresent(supertest, log, 4, [id]);
|
||||
const alertsOpen = await getAlertsById(supertest, log, id);
|
||||
expect(alertsOpen.hits.hits.length).to.eql(4);
|
||||
});
|
||||
|
||||
it('should copy the "dataset_name_1" from "event.dataset"', async () => {
|
||||
const rule: EqlRuleCreateProps = {
|
||||
...getEqlRuleForSignalTesting(['const_keyword']),
|
||||
...getEqlRuleForAlertTesting(['const_keyword']),
|
||||
query: 'any where event.dataset=="dataset_name_1"',
|
||||
};
|
||||
|
||||
const { id } = await createRule(supertest, log, rule);
|
||||
await waitForRuleSuccess({ supertest, log, id });
|
||||
await waitForSignalsToBePresent(supertest, log, 4, [id]);
|
||||
const signalsOpen = await getSignalsById(supertest, log, id);
|
||||
const hits = signalsOpen.hits.hits.map((hit) => hit._source?.['event.dataset']).sort();
|
||||
await waitForAlertsToBePresent(supertest, log, 4, [id]);
|
||||
const alertsOpen = await getAlertsById(supertest, log, id);
|
||||
const hits = alertsOpen.hits.hits.map((hit) => hit._source?.['event.dataset']).sort();
|
||||
expect(hits).to.eql([
|
||||
'dataset_name_1',
|
||||
'dataset_name_1',
|
||||
|
@ -122,7 +121,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
describe('"threshold" rule type', async () => {
|
||||
it('should detect the "dataset_name_1" from "event.dataset"', async () => {
|
||||
const rule: ThresholdRuleCreateProps = {
|
||||
...getThresholdRuleForSignalTesting(['const_keyword']),
|
||||
...getThresholdRuleForAlertTesting(['const_keyword']),
|
||||
threshold: {
|
||||
field: 'event.dataset',
|
||||
value: 1,
|
||||
|
@ -130,9 +129,9 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
};
|
||||
const { id } = await createRule(supertest, log, rule);
|
||||
await waitForRuleSuccess({ supertest, log, id });
|
||||
await waitForSignalsToBePresent(supertest, log, 1, [id]);
|
||||
const signalsOpen = await getSignalsById(supertest, log, id);
|
||||
const hits = signalsOpen.hits.hits
|
||||
await waitForAlertsToBePresent(supertest, log, 1, [id]);
|
||||
const alertsOpen = await getAlertsById(supertest, log, id);
|
||||
const hits = alertsOpen.hits.hits
|
||||
.map((hit) => hit._source?.[ALERT_THRESHOLD_RESULT] ?? null)
|
||||
.sort();
|
||||
expect(hits).to.eql([
|
|
@ -5,9 +5,8 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { FtrProviderContext } from '../../../common/ftr_provider_context';
|
||||
import { FtrProviderContext } from '../../../../../ftr_provider_context';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default ({ loadTestFile }: FtrProviderContext): void => {
|
||||
describe('Detection keyword family data types', function () {
|
||||
loadTestFile(require.resolve('./keyword'));
|
|
@ -13,21 +13,20 @@ import {
|
|||
ThresholdRuleCreateProps,
|
||||
} from '@kbn/security-solution-plugin/common/api/detection_engine';
|
||||
import { ALERT_THRESHOLD_RESULT } from '@kbn/security-solution-plugin/common/field_maps/field_names';
|
||||
import { FtrProviderContext } from '../../../common/ftr_provider_context';
|
||||
import {
|
||||
createRule,
|
||||
createSignalsIndex,
|
||||
createAlertsIndex,
|
||||
deleteAllRules,
|
||||
deleteAllAlerts,
|
||||
getEqlRuleForSignalTesting,
|
||||
getRuleForSignalTesting,
|
||||
getSignalsById,
|
||||
getThresholdRuleForSignalTesting,
|
||||
getEqlRuleForAlertTesting,
|
||||
getRuleForAlertTesting,
|
||||
getAlertsById,
|
||||
getThresholdRuleForAlertTesting,
|
||||
waitForRuleSuccess,
|
||||
waitForSignalsToBePresent,
|
||||
waitForAlertsToBePresent,
|
||||
} from '../../../utils';
|
||||
import { FtrProviderContext } from '../../../../../ftr_provider_context';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default ({ getService }: FtrProviderContext) => {
|
||||
const supertest = getService('supertest');
|
||||
const esArchiver = getService('esArchiver');
|
||||
|
@ -44,7 +43,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await createSignalsIndex(supertest, log);
|
||||
await createAlertsIndex(supertest, log);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
|
@ -52,17 +51,17 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
await deleteAllRules(supertest, log);
|
||||
});
|
||||
|
||||
describe('"kql" rule type', () => {
|
||||
describe('@ess @serverless "kql" rule type', () => {
|
||||
it('should detect the "dataset_name_1" from "event.dataset"', async () => {
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['keyword']),
|
||||
...getRuleForAlertTesting(['keyword']),
|
||||
query: 'event.dataset: "dataset_name_1"',
|
||||
};
|
||||
const { id } = await createRule(supertest, log, rule);
|
||||
await waitForRuleSuccess({ supertest, log, id });
|
||||
await waitForSignalsToBePresent(supertest, log, 4, [id]);
|
||||
const signalsOpen = await getSignalsById(supertest, log, id);
|
||||
const hits = signalsOpen.hits.hits.map((hit) => hit._source?.['event.dataset']).sort();
|
||||
await waitForAlertsToBePresent(supertest, log, 4, [id]);
|
||||
const alertsOpen = await getAlertsById(supertest, log, id);
|
||||
const hits = alertsOpen.hits.hits.map((hit) => hit._source?.['event.dataset']).sort();
|
||||
expect(hits).to.eql([
|
||||
'dataset_name_1',
|
||||
'dataset_name_1',
|
||||
|
@ -75,15 +74,15 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
describe('"eql" rule type', () => {
|
||||
it('should detect the "dataset_name_1" from "event.dataset"', async () => {
|
||||
const rule: EqlRuleCreateProps = {
|
||||
...getEqlRuleForSignalTesting(['keyword']),
|
||||
...getEqlRuleForAlertTesting(['keyword']),
|
||||
query: 'any where event.dataset=="dataset_name_1"',
|
||||
};
|
||||
|
||||
const { id } = await createRule(supertest, log, rule);
|
||||
await waitForRuleSuccess({ supertest, log, id });
|
||||
await waitForSignalsToBePresent(supertest, log, 4, [id]);
|
||||
const signalsOpen = await getSignalsById(supertest, log, id);
|
||||
const hits = signalsOpen.hits.hits.map((hit) => hit._source?.['event.dataset']).sort();
|
||||
await waitForAlertsToBePresent(supertest, log, 4, [id]);
|
||||
const alertsOpen = await getAlertsById(supertest, log, id);
|
||||
const hits = alertsOpen.hits.hits.map((hit) => hit._source?.['event.dataset']).sort();
|
||||
expect(hits).to.eql([
|
||||
'dataset_name_1',
|
||||
'dataset_name_1',
|
||||
|
@ -96,7 +95,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
describe('"threshold" rule type', async () => {
|
||||
it('should detect the "dataset_name_1" from "event.dataset"', async () => {
|
||||
const rule: ThresholdRuleCreateProps = {
|
||||
...getThresholdRuleForSignalTesting(['keyword']),
|
||||
...getThresholdRuleForAlertTesting(['keyword']),
|
||||
threshold: {
|
||||
field: 'event.dataset',
|
||||
value: 1,
|
||||
|
@ -104,9 +103,9 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
};
|
||||
const { id } = await createRule(supertest, log, rule);
|
||||
await waitForRuleSuccess({ supertest, log, id });
|
||||
await waitForSignalsToBePresent(supertest, log, 1, [id]);
|
||||
const signalsOpen = await getSignalsById(supertest, log, id);
|
||||
const hits = signalsOpen.hits.hits
|
||||
await waitForAlertsToBePresent(supertest, log, 1, [id]);
|
||||
const alertsOpen = await getAlertsById(supertest, log, id);
|
||||
const hits = alertsOpen.hits.hits
|
||||
.map((hit) => hit._source?.[ALERT_THRESHOLD_RESULT] ?? null)
|
||||
.sort();
|
||||
expect(hits).to.eql([
|
|
@ -12,27 +12,26 @@ import {
|
|||
} from '@kbn/security-solution-plugin/common/api/detection_engine';
|
||||
import { ALERT_THRESHOLD_RESULT } from '@kbn/security-solution-plugin/common/field_maps/field_names';
|
||||
|
||||
import { FtrProviderContext } from '../../../common/ftr_provider_context';
|
||||
import {
|
||||
createRule,
|
||||
createSignalsIndex,
|
||||
createAlertsIndex,
|
||||
deleteAllRules,
|
||||
deleteAllAlerts,
|
||||
getEqlRuleForSignalTesting,
|
||||
getRuleForSignalTesting,
|
||||
getSignalsById,
|
||||
getEqlRuleForAlertTesting,
|
||||
getRuleForAlertTesting,
|
||||
getAlertsById,
|
||||
waitForRuleSuccess,
|
||||
waitForSignalsToBePresent,
|
||||
waitForAlertsToBePresent,
|
||||
} from '../../../utils';
|
||||
import { FtrProviderContext } from '../../../../../ftr_provider_context';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default ({ getService }: FtrProviderContext) => {
|
||||
const supertest = getService('supertest');
|
||||
const esArchiver = getService('esArchiver');
|
||||
const log = getService('log');
|
||||
const es = getService('es');
|
||||
|
||||
describe('Rule detects against a keyword and constant_keyword of event.dataset', () => {
|
||||
describe('@ess @serverless Rule detects against a keyword and constant_keyword of event.dataset', () => {
|
||||
before(async () => {
|
||||
await esArchiver.load('x-pack/test/functional/es_archives/rule_keyword_family/const_keyword');
|
||||
await esArchiver.load('x-pack/test/functional/es_archives/rule_keyword_family/keyword');
|
||||
|
@ -46,7 +45,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await createSignalsIndex(supertest, log);
|
||||
await createAlertsIndex(supertest, log);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
|
@ -55,28 +54,28 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
});
|
||||
|
||||
describe('"kql" rule type', () => {
|
||||
it('should detect the "dataset_name_1" from "event.dataset" and have 8 signals, 4 from each index', async () => {
|
||||
it('should detect the "dataset_name_1" from "event.dataset" and have 8 alerts, 4 from each index', async () => {
|
||||
const rule = {
|
||||
...getRuleForSignalTesting(['keyword', 'const_keyword']),
|
||||
...getRuleForAlertTesting(['keyword', 'const_keyword']),
|
||||
query: 'event.dataset: "dataset_name_1"',
|
||||
};
|
||||
const { id } = await createRule(supertest, log, rule);
|
||||
await waitForRuleSuccess({ supertest, log, id });
|
||||
await waitForSignalsToBePresent(supertest, log, 8, [id]);
|
||||
const signalsOpen = await getSignalsById(supertest, log, id);
|
||||
expect(signalsOpen.hits.hits.length).to.eql(8);
|
||||
await waitForAlertsToBePresent(supertest, log, 8, [id]);
|
||||
const alertsOpen = await getAlertsById(supertest, log, id);
|
||||
expect(alertsOpen.hits.hits.length).to.eql(8);
|
||||
});
|
||||
|
||||
it('should copy the dataset_name_1 from the index into the signal', async () => {
|
||||
it('should copy the dataset_name_1 from the index into the alert', async () => {
|
||||
const rule = {
|
||||
...getRuleForSignalTesting(['keyword', 'const_keyword']),
|
||||
...getRuleForAlertTesting(['keyword', 'const_keyword']),
|
||||
query: 'event.dataset: "dataset_name_1"',
|
||||
};
|
||||
const { id } = await createRule(supertest, log, rule);
|
||||
await waitForRuleSuccess({ supertest, log, id });
|
||||
await waitForSignalsToBePresent(supertest, log, 8, [id]);
|
||||
const signalsOpen = await getSignalsById(supertest, log, id);
|
||||
const hits = signalsOpen.hits.hits.map((hit) => hit._source?.['event.dataset']).sort();
|
||||
await waitForAlertsToBePresent(supertest, log, 8, [id]);
|
||||
const alertsOpen = await getAlertsById(supertest, log, id);
|
||||
const hits = alertsOpen.hits.hits.map((hit) => hit._source?.['event.dataset']).sort();
|
||||
expect(hits).to.eql([
|
||||
'dataset_name_1',
|
||||
'dataset_name_1',
|
||||
|
@ -91,30 +90,30 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
});
|
||||
|
||||
describe('"eql" rule type', () => {
|
||||
it('should detect the "dataset_name_1" from "event.dataset" and have 8 signals, 4 from each index', async () => {
|
||||
it('should detect the "dataset_name_1" from "event.dataset" and have 8 alerts, 4 from each index', async () => {
|
||||
const rule: EqlRuleCreateProps = {
|
||||
...getEqlRuleForSignalTesting(['keyword', 'const_keyword']),
|
||||
...getEqlRuleForAlertTesting(['keyword', 'const_keyword']),
|
||||
query: 'any where event.dataset=="dataset_name_1"',
|
||||
};
|
||||
|
||||
const { id } = await createRule(supertest, log, rule);
|
||||
await waitForRuleSuccess({ supertest, log, id });
|
||||
await waitForSignalsToBePresent(supertest, log, 8, [id]);
|
||||
const signalsOpen = await getSignalsById(supertest, log, id);
|
||||
expect(signalsOpen.hits.hits.length).to.eql(8);
|
||||
await waitForAlertsToBePresent(supertest, log, 8, [id]);
|
||||
const alertsOpen = await getAlertsById(supertest, log, id);
|
||||
expect(alertsOpen.hits.hits.length).to.eql(8);
|
||||
});
|
||||
|
||||
it('should copy the "dataset_name_1" from "event.dataset"', async () => {
|
||||
const rule: EqlRuleCreateProps = {
|
||||
...getEqlRuleForSignalTesting(['keyword', 'const_keyword']),
|
||||
...getEqlRuleForAlertTesting(['keyword', 'const_keyword']),
|
||||
query: 'any where event.dataset=="dataset_name_1"',
|
||||
};
|
||||
|
||||
const { id } = await createRule(supertest, log, rule);
|
||||
await waitForRuleSuccess({ supertest, log, id });
|
||||
await waitForSignalsToBePresent(supertest, log, 8, [id]);
|
||||
const signalsOpen = await getSignalsById(supertest, log, id);
|
||||
const hits = signalsOpen.hits.hits.map((hit) => hit._source?.['event.dataset']).sort();
|
||||
await waitForAlertsToBePresent(supertest, log, 8, [id]);
|
||||
const alertsOpen = await getAlertsById(supertest, log, id);
|
||||
const hits = alertsOpen.hits.hits.map((hit) => hit._source?.['event.dataset']).sort();
|
||||
expect(hits).to.eql([
|
||||
'dataset_name_1',
|
||||
'dataset_name_1',
|
||||
|
@ -131,7 +130,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
describe('"threshold" rule type', async () => {
|
||||
it('should detect the "dataset_name_1" from "event.dataset"', async () => {
|
||||
const rule: ThresholdRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['keyword', 'const_keyword']),
|
||||
...getRuleForAlertTesting(['keyword', 'const_keyword']),
|
||||
rule_id: 'threshold-rule',
|
||||
type: 'threshold',
|
||||
language: 'kuery',
|
||||
|
@ -143,9 +142,9 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
};
|
||||
const { id } = await createRule(supertest, log, rule);
|
||||
await waitForRuleSuccess({ supertest, log, id });
|
||||
await waitForSignalsToBePresent(supertest, log, 1, [id]);
|
||||
const signalsOpen = await getSignalsById(supertest, log, id);
|
||||
const hits = signalsOpen.hits.hits
|
||||
await waitForAlertsToBePresent(supertest, log, 1, [id]);
|
||||
const alertsOpen = await getAlertsById(supertest, log, id);
|
||||
const hits = alertsOpen.hits.hits
|
||||
.map((hit) => hit._source?.[ALERT_THRESHOLD_RESULT] ?? null)
|
||||
.sort();
|
||||
expect(hits).to.eql([
|
|
@ -8,19 +8,18 @@
|
|||
import expect from '@kbn/expect';
|
||||
import { performance } from 'perf_hooks';
|
||||
|
||||
import { FtrProviderContext } from '../../common/ftr_provider_context';
|
||||
import {
|
||||
createRule,
|
||||
createSignalsIndex,
|
||||
createAlertsIndex,
|
||||
deleteAllRules,
|
||||
deleteAllAlerts,
|
||||
getRuleForSignalTesting,
|
||||
getSignalsById,
|
||||
getRuleForAlertTesting,
|
||||
getAlertsById,
|
||||
waitForRuleSuccess,
|
||||
waitForSignalsToBePresent,
|
||||
waitForAlertsToBePresent,
|
||||
} from '../../utils';
|
||||
import { FtrProviderContext } from '../../../../ftr_provider_context';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default ({ getService }: FtrProviderContext) => {
|
||||
const supertest = getService('supertest');
|
||||
const esArchiver = getService('esArchiver');
|
||||
|
@ -33,7 +32,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
}
|
||||
|
||||
// FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/138923
|
||||
describe('Tests involving runtime fields of source indexes and the signals index', () => {
|
||||
describe('@ess @serverless Tests involving runtime fields of source indexes and the alerts index', () => {
|
||||
before(async () => {
|
||||
await esArchiver.load('x-pack/test/functional/es_archives/security_solution/runtime');
|
||||
});
|
||||
|
@ -44,7 +43,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
|
||||
describe('Regular runtime field mappings', () => {
|
||||
beforeEach(async () => {
|
||||
await createSignalsIndex(supertest, log);
|
||||
await createAlertsIndex(supertest, log);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
|
@ -53,7 +52,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
});
|
||||
|
||||
it('should execute a rule to completion and not timeout when there are a lot of runtime fields', async () => {
|
||||
const rule = getRuleForSignalTesting(['runtime']);
|
||||
const rule = getRuleForAlertTesting(['runtime']);
|
||||
const { id } = await createRule(supertest, log, rule);
|
||||
const start = performance.now();
|
||||
await waitForRuleSuccess({ supertest, log, id });
|
||||
|
@ -61,26 +60,26 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
expect(end - start).to.be.lessThan(10000);
|
||||
});
|
||||
|
||||
it('should copy normal non-runtime data set from the source index into the signals index in the same position when the target is ECS compatible', async () => {
|
||||
const rule = getRuleForSignalTesting(['runtime']);
|
||||
it('should copy normal non-runtime data set from the source index into the alerts index in the same position when the target is ECS compatible', async () => {
|
||||
const rule = getRuleForAlertTesting(['runtime']);
|
||||
const { id } = await createRule(supertest, log, rule);
|
||||
await waitForRuleSuccess({ supertest, log, id });
|
||||
await waitForSignalsToBePresent(supertest, log, 4, [id]);
|
||||
const signalsOpen = await getSignalsById(supertest, log, id);
|
||||
const hits = signalsOpen.hits.hits
|
||||
.map((signal) => (signal._source?.host as Runtime).name)
|
||||
await waitForAlertsToBePresent(supertest, log, 4, [id]);
|
||||
const alertsOpen = await getAlertsById(supertest, log, id);
|
||||
const hits = alertsOpen.hits.hits
|
||||
.map((alert) => (alert._source?.host as Runtime).name)
|
||||
.sort();
|
||||
expect(hits).to.eql(['host name 1', 'host name 2', 'host name 3', 'host name 4']);
|
||||
});
|
||||
|
||||
it('should copy "runtime mapping" data from a source index into the signals index in the same position when the target is ECS compatible', async () => {
|
||||
const rule = getRuleForSignalTesting(['runtime']);
|
||||
it('should copy "runtime mapping" data from a source index into the alerts index in the same position when the target is ECS compatible', async () => {
|
||||
const rule = getRuleForAlertTesting(['runtime']);
|
||||
const { id } = await createRule(supertest, log, rule);
|
||||
await waitForRuleSuccess({ supertest, log, id });
|
||||
await waitForSignalsToBePresent(supertest, log, 4, [id]);
|
||||
const signalsOpen = await getSignalsById(supertest, log, id);
|
||||
const hits = signalsOpen.hits.hits
|
||||
.map((signal) => (signal._source?.host as Runtime).hostname)
|
||||
await waitForAlertsToBePresent(supertest, log, 4, [id]);
|
||||
const alertsOpen = await getAlertsById(supertest, log, id);
|
||||
const hits = alertsOpen.hits.hits
|
||||
.map((alert) => (alert._source?.host as Runtime).hostname)
|
||||
.sort();
|
||||
expect(hits).to.eql(['host name 1', 'host name 2', 'host name 3', 'host name 4']);
|
||||
});
|
||||
|
@ -88,7 +87,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
|
||||
describe('Runtime field mappings that have conflicts within them', () => {
|
||||
beforeEach(async () => {
|
||||
await createSignalsIndex(supertest, log);
|
||||
await createAlertsIndex(supertest, log);
|
||||
await esArchiver.load(
|
||||
'x-pack/test/functional/es_archives/security_solution/runtime_conflicting_fields'
|
||||
);
|
||||
|
@ -107,14 +106,14 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
* risk with overwriting fields in the strategy we are currently using in detection engine. If you swap, change the strategies
|
||||
* because we decide to overwrite "_source" values with "fields", then expect to change this test.
|
||||
*/
|
||||
it('should NOT copy normal non-runtime data set from the source index into the signals index in the same position when the target is ECS compatible', async () => {
|
||||
const rule = getRuleForSignalTesting(['runtime_conflicting_fields']);
|
||||
it('should NOT copy normal non-runtime data set from the source index into the alerts index in the same position when the target is ECS compatible', async () => {
|
||||
const rule = getRuleForAlertTesting(['runtime_conflicting_fields']);
|
||||
const { id } = await createRule(supertest, log, rule);
|
||||
await waitForRuleSuccess({ supertest, log, id });
|
||||
await waitForSignalsToBePresent(supertest, log, 4, [id]);
|
||||
const signalsOpen = await getSignalsById(supertest, log, id);
|
||||
const hits = signalsOpen.hits.hits
|
||||
.map((signal) => signal._source?.host as Array<{ name: string }>)
|
||||
await waitForAlertsToBePresent(supertest, log, 4, [id]);
|
||||
const alertsOpen = await getAlertsById(supertest, log, id);
|
||||
const hits = alertsOpen.hits.hits
|
||||
.map((alert) => alert._source?.host as Array<{ name: string }>)
|
||||
.map((host) => {
|
||||
// sort the inner array elements first
|
||||
return host.sort((a, b) => a.name.localeCompare(b.name));
|
||||
|
@ -164,15 +163,13 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
* fields as arrays of objects since the objects are flattened in "fields" and we detect something already there so we skip
|
||||
* this shadowed runtime data as it is ambiguous of where we would put it in the array.
|
||||
*/
|
||||
it('should NOT copy "runtime mapping" data from a source index into the signals index in the same position when the target is ECS compatible', async () => {
|
||||
const rule = getRuleForSignalTesting(['runtime_conflicting_fields']);
|
||||
it('should NOT copy "runtime mapping" data from a source index into the alerts index in the same position when the target is ECS compatible', async () => {
|
||||
const rule = getRuleForAlertTesting(['runtime_conflicting_fields']);
|
||||
const { id } = await createRule(supertest, log, rule);
|
||||
await waitForRuleSuccess({ supertest, log, id });
|
||||
await waitForSignalsToBePresent(supertest, log, 4, [id]);
|
||||
const signalsOpen = await getSignalsById(supertest, log, id);
|
||||
const hits = signalsOpen.hits.hits.map(
|
||||
(signal) => (signal._source?.host as Runtime).hostname
|
||||
);
|
||||
await waitForAlertsToBePresent(supertest, log, 4, [id]);
|
||||
const alertsOpen = await getAlertsById(supertest, log, id);
|
||||
const hits = alertsOpen.hits.hits.map((alert) => (alert._source?.host as Runtime).hostname);
|
||||
expect(hits).to.eql([undefined, undefined, undefined, undefined]);
|
||||
});
|
||||
});
|
|
@ -14,37 +14,41 @@ import {
|
|||
} from '@kbn/security-solution-plugin/common/api/detection_engine';
|
||||
import { ALERT_ORIGINAL_TIME } from '@kbn/security-solution-plugin/common/field_maps/field_names';
|
||||
|
||||
import { FtrProviderContext } from '../../common/ftr_provider_context';
|
||||
import {
|
||||
createSignalsIndex,
|
||||
createAlertsIndex,
|
||||
deleteAllRules,
|
||||
deleteAllAlerts,
|
||||
createRule,
|
||||
waitForRuleSuccess,
|
||||
waitForSignalsToBePresent,
|
||||
getOpenSignals,
|
||||
getRuleForSignalTesting,
|
||||
getSignalsByIds,
|
||||
getEqlRuleForSignalTesting,
|
||||
waitForAlertsToBePresent,
|
||||
getOpenAlerts,
|
||||
getRuleForAlertTesting,
|
||||
getAlertsByIds,
|
||||
getEqlRuleForAlertTesting,
|
||||
waitForRulePartialFailure,
|
||||
} from '../../utils';
|
||||
import { FtrProviderContext } from '../../../../ftr_provider_context';
|
||||
import { EsArchivePathBuilder } from '../../../../es_archive_path_builder';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default ({ getService }: FtrProviderContext) => {
|
||||
const supertest = getService('supertest');
|
||||
const esArchiver = getService('esArchiver');
|
||||
const es = getService('es');
|
||||
const log = getService('log');
|
||||
|
||||
// TODO: add a new service
|
||||
const config = getService('config');
|
||||
const isServerless = config.get('serverless');
|
||||
const dataPathBuilder = new EsArchivePathBuilder(isServerless);
|
||||
const path = dataPathBuilder.getPath('auditbeat/hosts');
|
||||
/**
|
||||
* Tests around timestamps within signals such as the copying of timestamps correctly into
|
||||
* Tests around timestamps within alerts such as the copying of timestamps correctly into
|
||||
* the "signal.original_time" field, ensuring that timestamp overrides operate, and ensuring that
|
||||
* partial errors happen correctly
|
||||
*/
|
||||
describe('timestamp tests', () => {
|
||||
describe('Signals generated from events with a timestamp in seconds is converted correctly into the forced ISO8601 format when copying', () => {
|
||||
describe('@ess @serverless timestamp tests', () => {
|
||||
describe('alerts generated from events with a timestamp in seconds is converted correctly into the forced ISO8601 format when copying', () => {
|
||||
beforeEach(async () => {
|
||||
await createSignalsIndex(supertest, log);
|
||||
await createAlertsIndex(supertest, log);
|
||||
await esArchiver.load(
|
||||
'x-pack/test/functional/es_archives/security_solution/timestamp_in_seconds'
|
||||
);
|
||||
|
@ -66,58 +70,50 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
|
||||
describe('KQL query', () => {
|
||||
it('should convert the @timestamp which is epoch_seconds into the correct ISO format', async () => {
|
||||
const rule = getRuleForSignalTesting(['timestamp_in_seconds']);
|
||||
const rule = getRuleForAlertTesting(['timestamp_in_seconds']);
|
||||
const { id } = await createRule(supertest, log, rule);
|
||||
await waitForRuleSuccess({ supertest, log, id });
|
||||
await waitForSignalsToBePresent(supertest, log, 1, [id]);
|
||||
const signalsOpen = await getSignalsByIds(supertest, log, [id]);
|
||||
const hits = signalsOpen.hits.hits
|
||||
.map((hit) => hit._source?.[ALERT_ORIGINAL_TIME])
|
||||
.sort();
|
||||
await waitForAlertsToBePresent(supertest, log, 1, [id]);
|
||||
const alertsOpen = await getAlertsByIds(supertest, log, [id]);
|
||||
const hits = alertsOpen.hits.hits.map((hit) => hit._source?.[ALERT_ORIGINAL_TIME]).sort();
|
||||
expect(hits).to.eql(['2021-06-02T23:33:15.000Z']);
|
||||
});
|
||||
|
||||
it('should still use the @timestamp field even with an override field. It should never use the override field', async () => {
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['myfakeindex-5']),
|
||||
...getRuleForAlertTesting(['myfakeindex-5']),
|
||||
timestamp_override: 'event.ingested',
|
||||
};
|
||||
const { id } = await createRule(supertest, log, rule);
|
||||
await waitForRuleSuccess({ supertest, log, id });
|
||||
await waitForSignalsToBePresent(supertest, log, 1, [id]);
|
||||
const signalsOpen = await getSignalsByIds(supertest, log, [id]);
|
||||
const hits = signalsOpen.hits.hits
|
||||
.map((hit) => hit._source?.[ALERT_ORIGINAL_TIME])
|
||||
.sort();
|
||||
await waitForAlertsToBePresent(supertest, log, 1, [id]);
|
||||
const alertsOpen = await getAlertsByIds(supertest, log, [id]);
|
||||
const hits = alertsOpen.hits.hits.map((hit) => hit._source?.[ALERT_ORIGINAL_TIME]).sort();
|
||||
expect(hits).to.eql(['2020-12-16T15:16:18.000Z']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('EQL query', () => {
|
||||
it('should convert the @timestamp which is epoch_seconds into the correct ISO format for EQL', async () => {
|
||||
const rule = getEqlRuleForSignalTesting(['timestamp_in_seconds']);
|
||||
const rule = getEqlRuleForAlertTesting(['timestamp_in_seconds']);
|
||||
const { id } = await createRule(supertest, log, rule);
|
||||
await waitForRuleSuccess({ supertest, log, id });
|
||||
await waitForSignalsToBePresent(supertest, log, 1, [id]);
|
||||
const signalsOpen = await getSignalsByIds(supertest, log, [id]);
|
||||
const hits = signalsOpen.hits.hits
|
||||
.map((hit) => hit._source?.[ALERT_ORIGINAL_TIME])
|
||||
.sort();
|
||||
await waitForAlertsToBePresent(supertest, log, 1, [id]);
|
||||
const alertsOpen = await getAlertsByIds(supertest, log, [id]);
|
||||
const hits = alertsOpen.hits.hits.map((hit) => hit._source?.[ALERT_ORIGINAL_TIME]).sort();
|
||||
expect(hits).to.eql(['2021-06-02T23:33:15.000Z']);
|
||||
});
|
||||
|
||||
it('should still use the @timestamp field even with an override field. It should never use the override field', async () => {
|
||||
const rule: EqlRuleCreateProps = {
|
||||
...getEqlRuleForSignalTesting(['myfakeindex-5']),
|
||||
...getEqlRuleForAlertTesting(['myfakeindex-5']),
|
||||
timestamp_override: 'event.ingested',
|
||||
};
|
||||
const { id } = await createRule(supertest, log, rule);
|
||||
await waitForRuleSuccess({ supertest, log, id });
|
||||
await waitForSignalsToBePresent(supertest, log, 1, [id]);
|
||||
const signalsOpen = await getSignalsByIds(supertest, log, [id]);
|
||||
const hits = signalsOpen.hits.hits
|
||||
.map((hit) => hit._source?.[ALERT_ORIGINAL_TIME])
|
||||
.sort();
|
||||
await waitForAlertsToBePresent(supertest, log, 1, [id]);
|
||||
const alertsOpen = await getAlertsByIds(supertest, log, [id]);
|
||||
const hits = alertsOpen.hits.hits.map((hit) => hit._source?.[ALERT_ORIGINAL_TIME]).sort();
|
||||
expect(hits).to.eql(['2020-12-16T15:16:18.000Z']);
|
||||
});
|
||||
});
|
||||
|
@ -129,10 +125,10 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
* If no timestamp override field exists in the indices but one was provided to the rule,
|
||||
* the rule's query will additionally search for events using the `@timestamp` field
|
||||
*/
|
||||
describe('Signals generated from events with timestamp override field', async () => {
|
||||
describe('alerts generated from events with timestamp override field', async () => {
|
||||
beforeEach(async () => {
|
||||
await deleteAllAlerts(supertest, log, es);
|
||||
await createSignalsIndex(supertest, log);
|
||||
await createAlertsIndex(supertest, log);
|
||||
await esArchiver.load(
|
||||
'x-pack/test/functional/es_archives/security_solution/timestamp_override_1'
|
||||
);
|
||||
|
@ -165,9 +161,9 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
});
|
||||
|
||||
describe('KQL', () => {
|
||||
it('should generate signals with event.ingested, @timestamp and (event.ingested + timestamp)', async () => {
|
||||
it('should generate alerts with event.ingested, @timestamp and (event.ingested + timestamp)', async () => {
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['myfa*']),
|
||||
...getRuleForAlertTesting(['myfa*']),
|
||||
timestamp_override: 'event.ingested',
|
||||
};
|
||||
|
||||
|
@ -178,17 +174,17 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
log,
|
||||
id,
|
||||
});
|
||||
await waitForSignalsToBePresent(supertest, log, 3, [id]);
|
||||
const signalsResponse = await getSignalsByIds(supertest, log, [id], 3);
|
||||
const signals = signalsResponse.hits.hits.map((hit) => hit._source);
|
||||
const signalsOrderedByEventId = orderBy(signals, 'signal.parent.id', 'asc');
|
||||
await waitForAlertsToBePresent(supertest, log, 3, [id]);
|
||||
const alertsResponse = await getAlertsByIds(supertest, log, [id], 3);
|
||||
const alerts = alertsResponse.hits.hits.map((hit) => hit._source);
|
||||
const alertsOrderedByEventId = orderBy(alerts, 'alert.parent.id', 'asc');
|
||||
|
||||
expect(signalsOrderedByEventId.length).equal(3);
|
||||
expect(alertsOrderedByEventId.length).equal(3);
|
||||
});
|
||||
|
||||
it('should generate 2 signals with event.ingested when timestamp fallback is disabled', async () => {
|
||||
it('should generate 2 alerts with event.ingested when timestamp fallback is disabled', async () => {
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['myfa*']),
|
||||
...getRuleForAlertTesting(['myfa*']),
|
||||
rule_id: 'rule-without-timestamp-fallback',
|
||||
timestamp_override: 'event.ingested',
|
||||
timestamp_override_fallback_disabled: true,
|
||||
|
@ -201,16 +197,16 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
log,
|
||||
id,
|
||||
});
|
||||
await waitForSignalsToBePresent(supertest, log, 2, [id]);
|
||||
const signalsResponse = await getSignalsByIds(supertest, log, [id], 2);
|
||||
const signals = signalsResponse.hits.hits.map((hit) => hit._source);
|
||||
const signalsOrderedByEventId = orderBy(signals, 'signal.parent.id', 'asc');
|
||||
await waitForAlertsToBePresent(supertest, log, 2, [id]);
|
||||
const alertsResponse = await getAlertsByIds(supertest, log, [id], 2);
|
||||
const alerts = alertsResponse.hits.hits.map((hit) => hit._source);
|
||||
const alertsOrderedByEventId = orderBy(alerts, 'alert.parent.id', 'asc');
|
||||
|
||||
expect(signalsOrderedByEventId.length).equal(2);
|
||||
expect(alertsOrderedByEventId.length).equal(2);
|
||||
});
|
||||
|
||||
it('should generate 2 signals with @timestamp', async () => {
|
||||
const rule: QueryRuleCreateProps = getRuleForSignalTesting(['myfa*']);
|
||||
it('should generate 2 alerts with @timestamp', async () => {
|
||||
const rule: QueryRuleCreateProps = getRuleForAlertTesting(['myfa*']);
|
||||
|
||||
const { id } = await createRule(supertest, log, rule);
|
||||
|
||||
|
@ -219,17 +215,17 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
log,
|
||||
id,
|
||||
});
|
||||
await waitForSignalsToBePresent(supertest, log, 2, [id]);
|
||||
const signalsResponse = await getSignalsByIds(supertest, log, [id]);
|
||||
const signals = signalsResponse.hits.hits.map((hit) => hit._source);
|
||||
const signalsOrderedByEventId = orderBy(signals, 'signal.parent.id', 'asc');
|
||||
await waitForAlertsToBePresent(supertest, log, 2, [id]);
|
||||
const alertsResponse = await getAlertsByIds(supertest, log, [id]);
|
||||
const alerts = alertsResponse.hits.hits.map((hit) => hit._source);
|
||||
const alertsOrderedByEventId = orderBy(alerts, 'alert.parent.id', 'asc');
|
||||
|
||||
expect(signalsOrderedByEventId.length).equal(2);
|
||||
expect(alertsOrderedByEventId.length).equal(2);
|
||||
});
|
||||
|
||||
it('should generate 2 signals when timestamp override does not exist', async () => {
|
||||
it('should generate 2 alerts when timestamp override does not exist', async () => {
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['myfa*']),
|
||||
...getRuleForAlertTesting(['myfa*']),
|
||||
timestamp_override: 'event.fakeingestfield',
|
||||
};
|
||||
const { id } = await createRule(supertest, log, rule);
|
||||
|
@ -239,50 +235,50 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
log,
|
||||
id,
|
||||
});
|
||||
await waitForSignalsToBePresent(supertest, log, 2, [id]);
|
||||
const signalsResponse = await getSignalsByIds(supertest, log, [id, id]);
|
||||
const signals = signalsResponse.hits.hits.map((hit) => hit._source);
|
||||
const signalsOrderedByEventId = orderBy(signals, 'signal.parent.id', 'asc');
|
||||
await waitForAlertsToBePresent(supertest, log, 2, [id]);
|
||||
const alertsResponse = await getAlertsByIds(supertest, log, [id, id]);
|
||||
const alerts = alertsResponse.hits.hits.map((hit) => hit._source);
|
||||
const alertsOrderedByEventId = orderBy(alerts, 'alert.parent.id', 'asc');
|
||||
|
||||
expect(signalsOrderedByEventId.length).equal(2);
|
||||
expect(alertsOrderedByEventId.length).equal(2);
|
||||
});
|
||||
|
||||
it('should not generate any signals when timestamp override does not exist and timestamp fallback is disabled', async () => {
|
||||
it('should not generate any alerts when timestamp override does not exist and timestamp fallback is disabled', async () => {
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['myfa*']),
|
||||
...getRuleForAlertTesting(['myfa*']),
|
||||
rule_id: 'rule-without-timestamp-fallback',
|
||||
timestamp_override: 'event.fakeingestfield',
|
||||
timestamp_override_fallback_disabled: true,
|
||||
};
|
||||
|
||||
const createdRule = await createRule(supertest, log, rule);
|
||||
const signalsOpen = await getOpenSignals(
|
||||
const alertsOpen = await getOpenAlerts(
|
||||
supertest,
|
||||
log,
|
||||
es,
|
||||
createdRule,
|
||||
RuleExecutionStatusEnum['partial failure']
|
||||
);
|
||||
expect(signalsOpen.hits.hits.length).eql(0);
|
||||
expect(alertsOpen.hits.hits.length).eql(0);
|
||||
});
|
||||
|
||||
/**
|
||||
* We should not use the timestamp override as the "original_time" as that can cause
|
||||
* confusion if you have both a timestamp and an override in the source event. Instead the "original_time"
|
||||
* field should only be overridden by the "timestamp" since when we generate a signal
|
||||
* and we add a new timestamp to the signal.
|
||||
* field should only be overridden by the "timestamp" since when we generate a alert
|
||||
* and we add a new timestamp to the alert.
|
||||
*/
|
||||
it('should NOT use the timestamp override as the "original_time"', async () => {
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['myfakeindex-2']),
|
||||
...getRuleForAlertTesting(['myfakeindex-2']),
|
||||
timestamp_override: 'event.ingested',
|
||||
};
|
||||
const { id } = await createRule(supertest, log, rule);
|
||||
|
||||
await waitForRuleSuccess({ supertest, log, id });
|
||||
await waitForSignalsToBePresent(supertest, log, 1, [id]);
|
||||
const signalsResponse = await getSignalsByIds(supertest, log, [id, id]);
|
||||
const hits = signalsResponse.hits.hits
|
||||
await waitForAlertsToBePresent(supertest, log, 1, [id]);
|
||||
const alertsResponse = await getAlertsByIds(supertest, log, [id, id]);
|
||||
const hits = alertsResponse.hits.hits
|
||||
.map((hit) => hit._source?.[ALERT_ORIGINAL_TIME])
|
||||
.sort();
|
||||
expect(hits).to.eql([undefined]);
|
||||
|
@ -290,8 +286,8 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
});
|
||||
|
||||
describe('EQL', () => {
|
||||
it('should generate 2 signals with @timestamp', async () => {
|
||||
const rule: EqlRuleCreateProps = getEqlRuleForSignalTesting(['myfa*']);
|
||||
it('should generate 2 alerts with @timestamp', async () => {
|
||||
const rule: EqlRuleCreateProps = getEqlRuleForAlertTesting(['myfa*']);
|
||||
|
||||
const { id } = await createRule(supertest, log, rule);
|
||||
|
||||
|
@ -300,17 +296,17 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
log,
|
||||
id,
|
||||
});
|
||||
await waitForSignalsToBePresent(supertest, log, 2, [id]);
|
||||
const signalsResponse = await getSignalsByIds(supertest, log, [id]);
|
||||
const signals = signalsResponse.hits.hits.map((hit) => hit._source);
|
||||
const signalsOrderedByEventId = orderBy(signals, 'signal.parent.id', 'asc');
|
||||
await waitForAlertsToBePresent(supertest, log, 2, [id]);
|
||||
const alertsResponse = await getAlertsByIds(supertest, log, [id]);
|
||||
const alerts = alertsResponse.hits.hits.map((hit) => hit._source);
|
||||
const alertsOrderedByEventId = orderBy(alerts, 'alert.parent.id', 'asc');
|
||||
|
||||
expect(signalsOrderedByEventId.length).equal(2);
|
||||
expect(alertsOrderedByEventId.length).equal(2);
|
||||
});
|
||||
|
||||
it('should generate 2 signals when timestamp override does not exist', async () => {
|
||||
it('should generate 2 alerts when timestamp override does not exist', async () => {
|
||||
const rule: EqlRuleCreateProps = {
|
||||
...getEqlRuleForSignalTesting(['myfa*']),
|
||||
...getEqlRuleForAlertTesting(['myfa*']),
|
||||
timestamp_override: 'event.fakeingestfield',
|
||||
};
|
||||
const { id } = await createRule(supertest, log, rule);
|
||||
|
@ -320,44 +316,44 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
log,
|
||||
id,
|
||||
});
|
||||
await waitForSignalsToBePresent(supertest, log, 2, [id]);
|
||||
const signalsResponse = await getSignalsByIds(supertest, log, [id, id]);
|
||||
const signals = signalsResponse.hits.hits.map((hit) => hit._source);
|
||||
const signalsOrderedByEventId = orderBy(signals, 'signal.parent.id', 'asc');
|
||||
await waitForAlertsToBePresent(supertest, log, 2, [id]);
|
||||
const alertsResponse = await getAlertsByIds(supertest, log, [id, id]);
|
||||
const alerts = alertsResponse.hits.hits.map((hit) => hit._source);
|
||||
const alertsOrderedByEventId = orderBy(alerts, 'alert.parent.id', 'asc');
|
||||
|
||||
expect(signalsOrderedByEventId.length).equal(2);
|
||||
expect(alertsOrderedByEventId.length).equal(2);
|
||||
});
|
||||
|
||||
it('should not generate any signals when timestamp override does not exist and timestamp fallback is disabled', async () => {
|
||||
it('should not generate any alerts when timestamp override does not exist and timestamp fallback is disabled', async () => {
|
||||
const rule: EqlRuleCreateProps = {
|
||||
...getEqlRuleForSignalTesting(['myfa*']),
|
||||
...getEqlRuleForAlertTesting(['myfa*']),
|
||||
timestamp_override: 'event.fakeingestfield',
|
||||
timestamp_override_fallback_disabled: true,
|
||||
};
|
||||
const createdRule = await createRule(supertest, log, rule);
|
||||
const signalsOpen = await getOpenSignals(
|
||||
const alertsOpen = await getOpenAlerts(
|
||||
supertest,
|
||||
log,
|
||||
es,
|
||||
createdRule,
|
||||
RuleExecutionStatusEnum['partial failure']
|
||||
);
|
||||
expect(signalsOpen.hits.hits.length).eql(0);
|
||||
expect(alertsOpen.hits.hits.length).eql(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Signals generated from events with timestamp override field and ensures search_after continues to work when documents are missing timestamp override field', () => {
|
||||
describe('alerts generated from events with timestamp override field and ensures search_after continues to work when documents are missing timestamp override field', () => {
|
||||
before(async () => {
|
||||
await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/hosts');
|
||||
await esArchiver.load(path);
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/hosts');
|
||||
await esArchiver.unload(path);
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await createSignalsIndex(supertest, log);
|
||||
await createAlertsIndex(supertest, log);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
|
@ -377,9 +373,9 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
*
|
||||
* ref: https://github.com/elastic/elasticsearch/issues/28806#issuecomment-369303620
|
||||
*/
|
||||
it('should generate 200 signals when timestamp override does not exist', async () => {
|
||||
it('should generate 200 alerts when timestamp override does not exist', async () => {
|
||||
const rule: QueryRuleCreateProps = {
|
||||
...getRuleForSignalTesting(['auditbeat-*']),
|
||||
...getRuleForAlertTesting(['auditbeat-*']),
|
||||
timestamp_override: 'event.fakeingested',
|
||||
max_signals: 200,
|
||||
};
|
||||
|
@ -390,11 +386,11 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
log,
|
||||
id,
|
||||
});
|
||||
await waitForSignalsToBePresent(supertest, log, 200, [id]);
|
||||
const signalsResponse = await getSignalsByIds(supertest, log, [id], 200);
|
||||
const signals = signalsResponse.hits.hits.map((hit) => hit._source);
|
||||
await waitForAlertsToBePresent(supertest, log, 200, [id]);
|
||||
const alertsResponse = await getAlertsByIds(supertest, log, [id], 200);
|
||||
const alerts = alertsResponse.hits.hits.map((hit) => hit._source);
|
||||
|
||||
expect(signals.length).equal(200);
|
||||
expect(alerts.length).equal(200);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { Client } from '@elastic/elasticsearch';
|
||||
import { ALERT_RULE_UUID } from '@kbn/rule-data-utils';
|
||||
import { DetectionAlert } from '@kbn/security-solution-plugin/common/api/detection_engine';
|
||||
import { RiskEnrichmentFields } from '@kbn/security-solution-plugin/server/lib/detection_engine/rule_types/utils/enrichments/types';
|
||||
import { refreshIndex } from '../refresh_index';
|
||||
|
||||
/**
|
||||
* Refresh an index, making changes available to search.
|
||||
* Useful for tests where we want to ensure that a rule does NOT create alerts, e.g. testing exceptions.
|
||||
* @param es The ElasticSearch handle
|
||||
*/
|
||||
export const getPreviewAlerts = async ({
|
||||
es,
|
||||
previewId,
|
||||
size,
|
||||
sort,
|
||||
}: {
|
||||
es: Client;
|
||||
previewId: string;
|
||||
size?: number;
|
||||
sort?: string[];
|
||||
}) => {
|
||||
const index = '.preview.alerts-security.alerts-*';
|
||||
await refreshIndex(es, index);
|
||||
const query = {
|
||||
bool: {
|
||||
filter: {
|
||||
term: {
|
||||
[ALERT_RULE_UUID]: previewId,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const result = await es.search<DetectionAlert & RiskEnrichmentFields>({
|
||||
index,
|
||||
size,
|
||||
query,
|
||||
sort,
|
||||
});
|
||||
return result.hits.hits;
|
||||
};
|
|
@ -14,9 +14,10 @@ export * from './get_open_alerts';
|
|||
export * from './get_alerts_by_ids';
|
||||
export * from './get_query_alerts_ids';
|
||||
export * from './get_alerts_by_id';
|
||||
export * from './remove_random_valued_properties';
|
||||
export * from './remove_random_valued_properties_from_alert';
|
||||
export * from './set_alert_status';
|
||||
export * from './get_alert_status_empty_response';
|
||||
export * from './get_query_alert_ids';
|
||||
export * from './set_alert_tags';
|
||||
export * from './get_preview_alerts';
|
||||
export * from './migrations';
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import { DetectionAlert } from '@kbn/security-solution-plugin/common/api/detection_engine';
|
||||
import { ALERT_LAST_DETECTED, ALERT_START } from '@kbn/rule-data-utils';
|
||||
|
||||
export const removeRandomValuedProperties = (alert: DetectionAlert | undefined) => {
|
||||
export const removeRandomValuedPropertiesFromAlert = (alert: DetectionAlert | undefined) => {
|
||||
if (!alert) {
|
||||
return undefined;
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ export const removeRandomValuedProperties = (alert: DetectionAlert | undefined)
|
|||
'kibana.alert.rule.created_at': createdAt,
|
||||
'kibana.alert.rule.updated_at': updatedAt,
|
||||
'kibana.alert.uuid': alertUuid,
|
||||
'kibana.alert.url': alertURL,
|
||||
[ALERT_START]: alertStart,
|
||||
[ALERT_LAST_DETECTED]: lastDetected,
|
||||
...restOfAlert
|
|
@ -9,6 +9,7 @@ export * from './exception_list_and_item';
|
|||
export * from './alerts';
|
||||
export * from './actions';
|
||||
export * from './data_generator';
|
||||
export * from './machine_learning';
|
||||
|
||||
export * from './rules/get_rule_so_by_id';
|
||||
export * from './rules/create_rule_saved_object';
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
export * from './machine_learning_setup';
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type SuperTest from 'supertest';
|
||||
import { getCommonRequestHeader } from '../../../../../functional/services/ml/common_api';
|
||||
|
||||
export const executeSetupModuleRequest = async ({
|
||||
module,
|
||||
rspCode,
|
||||
supertest,
|
||||
}: {
|
||||
module: string;
|
||||
rspCode: number;
|
||||
supertest: SuperTest.SuperTest<SuperTest.Test>;
|
||||
}) => {
|
||||
const { body } = await supertest
|
||||
.post(`/internal/ml/modules/setup/${module}`)
|
||||
.set(getCommonRequestHeader('1'))
|
||||
.send({
|
||||
prefix: '',
|
||||
groups: ['auditbeat'],
|
||||
indexPatternName: 'auditbeat-*',
|
||||
startDatafeed: false,
|
||||
useDedicatedIndex: true,
|
||||
applyToAllSpaces: true,
|
||||
})
|
||||
.expect(rspCode);
|
||||
|
||||
return body;
|
||||
};
|
||||
|
||||
export const forceStartDatafeeds = async ({
|
||||
jobId,
|
||||
rspCode,
|
||||
supertest,
|
||||
}: {
|
||||
jobId: string;
|
||||
rspCode: number;
|
||||
supertest: SuperTest.SuperTest<SuperTest.Test>;
|
||||
}) => {
|
||||
const { body } = await supertest
|
||||
.post(`/internal/ml/jobs/force_start_datafeeds`)
|
||||
.set(getCommonRequestHeader('1'))
|
||||
.send({
|
||||
datafeedIds: [`datafeed-${jobId}`],
|
||||
start: new Date().getUTCMilliseconds(),
|
||||
})
|
||||
.expect(rspCode);
|
||||
|
||||
return body;
|
||||
};
|
|
@ -19,7 +19,7 @@ export const getRuleForAlertTesting = (
|
|||
ruleId = 'rule-1',
|
||||
enabled = true
|
||||
): QueryRuleCreateProps => ({
|
||||
name: 'Signal Testing Query',
|
||||
name: 'Alert Testing Query',
|
||||
description: 'Tests a simple query',
|
||||
enabled,
|
||||
risk_score: 1,
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { QueryRuleCreateProps } from '@kbn/security-solution-plugin/common/api/detection_engine';
|
||||
|
||||
export const getRuleForAlertTestingWithTimestampOverride = (
|
||||
index: string[],
|
||||
ruleId = 'rule-1',
|
||||
enabled = true,
|
||||
timestampOverride = 'event.ingested'
|
||||
): QueryRuleCreateProps => ({
|
||||
name: 'Alert Testing Query',
|
||||
description: 'Tests a simple query',
|
||||
enabled,
|
||||
risk_score: 1,
|
||||
rule_id: ruleId,
|
||||
severity: 'high',
|
||||
index,
|
||||
type: 'query',
|
||||
query: '*:*',
|
||||
timestamp_override: timestampOverride,
|
||||
from: '1900-01-01T00:00:00.000Z',
|
||||
});
|
|
@ -31,6 +31,8 @@ export * from './get_saved_query_rule_for_alert_testing';
|
|||
export * from './get_rule_so_by_id';
|
||||
export * from './create_rule_saved_object';
|
||||
export * from './get_rule_with_legacy_investigation_fields';
|
||||
export * from './preview_rule';
|
||||
export * from './preview_rule_with_exception_entries';
|
||||
export * from './patch_rule';
|
||||
export * from './generate_event';
|
||||
|
||||
export * from './prebuilt_rules';
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type SuperTest from 'supertest';
|
||||
import type {
|
||||
RuleCreateProps,
|
||||
PreviewRulesSchema,
|
||||
RulePreviewLogs,
|
||||
} from '@kbn/security-solution-plugin/common/api/detection_engine';
|
||||
|
||||
import { DETECTION_ENGINE_RULES_PREVIEW } from '@kbn/security-solution-plugin/common/constants';
|
||||
|
||||
/**
|
||||
* Runs the preview for a rule. Any generated alerts will be written to .preview.alerts.
|
||||
* This is much faster than actually running the rule, and can also quickly simulate multiple
|
||||
* consecutive rule runs, e.g. for ensuring that rule state is properly handled across runs.
|
||||
* @param supertest The supertest deps
|
||||
* @param rule The rule to create
|
||||
*/
|
||||
export const previewRule = async ({
|
||||
supertest,
|
||||
rule,
|
||||
invocationCount = 1,
|
||||
timeframeEnd = new Date(),
|
||||
}: {
|
||||
supertest: SuperTest.SuperTest<SuperTest.Test>;
|
||||
rule: RuleCreateProps;
|
||||
invocationCount?: number;
|
||||
timeframeEnd?: Date;
|
||||
}): Promise<{
|
||||
previewId: string;
|
||||
logs: RulePreviewLogs[];
|
||||
isAborted: boolean;
|
||||
}> => {
|
||||
const previewRequest: PreviewRulesSchema = {
|
||||
...rule,
|
||||
invocationCount,
|
||||
timeframeEnd: timeframeEnd.toISOString(),
|
||||
};
|
||||
const response = await supertest
|
||||
.post(DETECTION_ENGINE_RULES_PREVIEW)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.set('elastic-api-version', '2023-10-31')
|
||||
.send(previewRequest)
|
||||
.expect(200);
|
||||
return response.body;
|
||||
};
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { ToolingLog } from '@kbn/tooling-log';
|
||||
import type SuperTest from 'supertest';
|
||||
import type { NonEmptyEntriesArray, OsTypeArray } from '@kbn/securitysolution-io-ts-list-types';
|
||||
import type { RuleCreateProps } from '@kbn/security-solution-plugin/common/api/detection_engine';
|
||||
|
||||
import {
|
||||
createContainerWithEntries,
|
||||
createContainerWithEndpointEntries,
|
||||
} from '../exception_list_and_item';
|
||||
import { previewRule } from './preview_rule';
|
||||
|
||||
/**
|
||||
* Convenience testing function where you can pass in just the entries and you will
|
||||
* get a rule created with the entries added to an exception list and exception list item
|
||||
* all auto-created at once.
|
||||
* @param supertest super test agent
|
||||
* @param rule The rule to create and attach an exception list to
|
||||
* @param entries The entries to create the rule and exception list from
|
||||
* @param endpointEntries The endpoint entries to create the rule and exception list from
|
||||
* @param osTypes The os types to optionally add or not to add to the container
|
||||
*/
|
||||
export const previewRuleWithExceptionEntries = async ({
|
||||
supertest,
|
||||
log,
|
||||
rule,
|
||||
entries,
|
||||
endpointEntries,
|
||||
invocationCount,
|
||||
timeframeEnd,
|
||||
}: {
|
||||
supertest: SuperTest.SuperTest<SuperTest.Test>;
|
||||
log: ToolingLog;
|
||||
rule: RuleCreateProps;
|
||||
entries: NonEmptyEntriesArray[];
|
||||
endpointEntries?: Array<{
|
||||
entries: NonEmptyEntriesArray;
|
||||
osTypes: OsTypeArray | undefined;
|
||||
}>;
|
||||
invocationCount?: number;
|
||||
timeframeEnd?: Date;
|
||||
}) => {
|
||||
const maybeExceptionList = await createContainerWithEntries(supertest, log, entries);
|
||||
const maybeEndpointList = await createContainerWithEndpointEntries(
|
||||
supertest,
|
||||
log,
|
||||
endpointEntries ?? []
|
||||
);
|
||||
|
||||
return previewRule({
|
||||
supertest,
|
||||
rule: {
|
||||
...rule,
|
||||
exceptions_list: [...maybeExceptionList, ...maybeEndpointList],
|
||||
},
|
||||
invocationCount,
|
||||
timeframeEnd,
|
||||
});
|
||||
};
|
|
@ -30,6 +30,8 @@
|
|||
"@kbn/core-saved-objects-server",
|
||||
"@kbn/core",
|
||||
"@kbn/alerting-plugin",
|
||||
"@kbn/securitysolution-ecs",
|
||||
"@kbn/securitysolution-rules",
|
||||
"@kbn/core-http-common",
|
||||
"@kbn/securitysolution-ecs",
|
||||
"@kbn/fleet-plugin",
|
||||
|
|
|
@ -86,7 +86,6 @@
|
|||
"@kbn/data-views-plugin",
|
||||
"@kbn/datemath",
|
||||
"@kbn/safer-lodash-set",
|
||||
"@kbn/securitysolution-rules",
|
||||
"@kbn/es-archiver",
|
||||
"@kbn/config-schema",
|
||||
"@kbn/kubernetes-security-plugin",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue