From 8659d737e5ff1793b5aa356139c83f6cf20a7c13 Mon Sep 17 00:00:00 2001 From: Andrew Goldstein Date: Thu, 10 Oct 2019 15:45:15 -0600 Subject: [PATCH] ## [SIEM] Endgame Row Renderers: DNS, File (FIM), Network, Security (Authentication), Process This PR renders Endgame events via _row renderers_ in the Timeline, per the following screenshot: ![endgame-row-renderers](https://user-images.githubusercontent.com/4459398/66854649-fa6d7900-ef3e-11e9-97cc-5b229041f186.png) The following Endgame event types / subtypes will be rendered via row renderers in the Timeline: * DNS (`dns_event`) - [X] `request_event` * File (FIM) (`file_event`) - [X] `file_create_event` - [X] `file_delete_event` * Network (`network_event`) - [X] `ipv4_connection_accept_event` - [X] `ipv6_connection_accept_event` - [X] `ipv4_disconnect_received_event` - [X] `ipv6_disconnect_received_event` * Security (Authentication) (`security_event`) - [X] `user_logon` - [X] `admin_logon` - [X] `explicit_user_logon` - [X] `user_logoff` * Process (`process_event`) - [X] `creation_event` - [X] `termination_event` This PR also adds row rendering support for some non-Endgame events that conform to the [Elastic Common Schema](https://www.elastic.co/guide/en/ecs/current/index.html) (ECS): * DNS requests * FIM file creation events * FIM file deletion events RELEASE NOTE: To view Endgame events in existing SIEM deployments, you must manually add `endgame-*` to the SIEM index pattern in `Kibana Management > Advanced Settings > SIEM > Elasticsearch indices`. ## DNS Request events Endgame DNS events with the following event type and subtype will be rendered in the Timeline via row renderers: ``` endgame.event_type_full: dns_event and endgame.event_subtype_full: request_event ``` _To view these Endgame DNS events in a timeline, add `endgame-*` to the `SIEM` > `Elasticsearch indices` setting in Kibana `Advanced Settings`, then paste the query above into a timeline to view events._ ### Runtime matching criteria All DNS events, including Endgame and non-Endgame DNS events matching the following criteria will be rendered: ``` dns.question.type: * and dns.question.name: * ``` _The query above can be executed in a timeline to view all data that will be rendered via the (new) DNS event row renderer._ ### Sample rendered DNS event ![endgame-dns-event](https://user-images.githubusercontent.com/4459398/66856414-643b5200-ef42-11e9-8d50-894b7f7abf3d.png) Each field with `this formatting` will be draggable (to pivot a search) in the row-rendered event: `Arun` \ `Anvi-Acer` @ `HD-obe-8bf77f54` asked for `clients4.google.com` with question type `A`, which resolved to `10.58.197.78` (response code: `NOERROR`) via `chrome.exe` `(11620)` [![windows-logo](https://user-images.githubusercontent.com/4459398/66249835-e3d15180-e6f6-11e9-89c3-5517c5ed1596.png) `3008`] ### Fields in a DNS event The following fields will be used to render a DNS event: `user.name` \ `user.domain` @ `host.name` asked for `dns.question.name` with question type `dns.question.type`, which resolved to `dns.resolved_ip` (resp code: `dns.response_code`) via `process.name` `(process.pid)` [![windows-logo](https://user-images.githubusercontent.com/4459398/66249835-e3d15180-e6f6-11e9-89c3-5517c5ed1596.png) `event.code | winlog.event_id`] Note: At the time of this writing, Endgame DNS events do not populate `dns.response_code`. Row renderers are designed to still render partial results when fields are missing. In this case the following text: > (resp code: `dns.response_code`) will NOT be rendered, but the other (populated) fields in the DNS event will be rendered. ### Additional Rendering of DNS events by the Netflow row renderer In addition to being rendered by the new DNS renderer described above, DNS events will also be rendered by the Netflow row renderer. The Neflow row renderer shows the directionality, protocol, and flow of data between a source and destination ### Non-Endgame DNS events The following screenshot shows a DNS event from `packetbeat` rendered by the new DNS row renderer: ![non-endgame-dns-event](https://user-images.githubusercontent.com/4459398/66857061-b7fa6b00-ef43-11e9-894a-d717539db96c.png) _A non-Endgame DNS event that conforms to ECS_ ## File (FIM) Creation events Endgame File (FIM) Creation events with the following event type and subtype will be rendered in the Timeline via row renderers: ``` endgame.event_type_full: file_event and endgame.event_subtype_full: file_create_event ``` ### Runtime matching criteria All file creation events, including Endgame and non-Endgame events matching the following criteria will be rendered: ``` (event.category: file and event.action: file_create_event) or (event.dataset: file and event.action: created) ``` ### Sample rendered File (FIM) Creation event ![file-create-event](https://user-images.githubusercontent.com/4459398/66857794-3f94a980-ef45-11e9-9030-fff35403e8f4.png) `Arun` \ `Anvi-Acer` @ `HD-obe-8bf77f54` created file `the-real-index~RFa99cd75.TMP` in `C:\Users\Arun\AppData\Local\Google\Chrome\User Data\Default\Service Worker\CacheStorage\579544fd7d0441717f082c9eb123588966aa57ac\d81a98b1-59b9-43b2-a228-b3daf7da56df\index-dir\the-real-index~RFa99cd75.TMP` via `chrome.exe` `(11620)` ### Fields in a File (FIM) Creation event `user.name` \ `user.domain` @ `host.name` created file `file.name | endgame.file_name` in `file.path | endgame.file_path` via `process.name | endgame.process_name` `(process.pid | endgame.pid)` ## File (FIM) Deletion events Endgame File (FIM) Deletion events with the following event type and subtype will be rendered in the Timeline via row renderers: ``` endgame.event_type_full: file_event and endgame.event_subtype_full: file_delete_event ``` ### Runtime matching criteria All file deletion events, including Endgame and non-Endgame events matching the following criteria will be rendered: ``` (event.category: file and event.action: file_delete_event) or (event.dataset: file and event.action: deleted) ``` ### Sample rendered File (FIM) Deletion event ![file-delete-event](https://user-images.githubusercontent.com/4459398/66857970-9a2e0580-ef45-11e9-97bb-219c8673a2f2.png) `SYSTEM` \ `NT AUTHORITY` @ `HD-v1s-d2118419` deleted file `tmp0000031a` in `C:\Windows\TEMP\tmp00000404\tmp0000031a` via `AmSvc.exe` `(1084)` ### Fields in a File (FIM) Deletion event `user.name` \ `user.domain` @ `host.name` deleted file `file.name | endgame.file_name` in `file.path | endgame.file_path` via `process.name | endgame.process_name` `(process.pid | endgame.pid)` ## Network Connection Accepted events Endgame Network Connection Accepted events with the following event type and subtype will be rendered in the Timeline via row renderers: ``` (endgame.event_type_full: network_event and endgame.event_subtype_full: ipv4_connection_accept_event) or (endgame.event_type_full: network_event and endgame.event_subtype_full: ipv6_connection_accept_event) ```` ### Runtime matching criteria All Endgame Connection Accepted events, and existing "socket opened" events matching the following criteria will be rendered: ``` event.action: ipv4_connection_accept_event or event.action: ipv6_connection_accept_event or event.action: socket_opened ``` ### Sample rendered Network Connection Accepted event ![ipv4-connection-accept-event](https://user-images.githubusercontent.com/4459398/66858241-16c0e400-ef46-11e9-9fa8-f8b852490bd8.png) `SYSTEM` \ `NT AUTHORITY` @ `HD-gqf-0af7b4fe` accepted a connection via `AmSvc.exe` `(1084)` Network Connection Accepted events are also be rendered with the Netflow row renderer, like the `event.action: socket_opened` events are rendered today. The Network Connection Accepted row renderer displays information about the principal actors in the event (i.e. `user.name`, `host.name`, `process.name`), and the Netflow row renderer displays information about the directionality, source / destination, protocol, etc. ### Fields in a Network Connection Accepted event `user.name` \ `user.domain` @ `host.name` accepted a connection via `process.name` `(process.pid)` ## Network Disconnect Received events Endgame Network Disconnect Received events with the following event type and subtype will be rendered in the Timeline via row renderers: ``` (endgame.event_type_full: network_event and endgame.event_subtype_full: ipv4_disconnect_received_event) or (endgame.event_type_full: network_event and endgame.event_subtype_full: ipv6_disconnect_received_event) ```` ### Runtime matching criteria All Endgame Network Disconnect Received events, and existing "socket closed" events matching the following criteria will be rendered: ``` event.action: ipv4_disconnect_received_event or event.action: ipv6_disconnect_received_event or event.action: socket_closed ``` ### Sample rendered Network Disconnect Received event ![ipv4-disconnect-received-event](https://user-images.githubusercontent.com/4459398/66859155-fa25ab80-ef47-11e9-995c-7628fc0885bf.png) `SYSTEM` \ `NT AUTHORITY` @ `HD-gqf-0af7b4fe` disconnected via `AmSvc.exe` `(1084)` The existing row renderer for `event.action: socket_closed` will be enhanced to display additional fields: - `user.domain` - `process.pid` Network Disconnect Received events will also be rendered with the Netflow row renderer, like the `event.action: socket_closed` events are rendered today. The Network Connection Accepted row renderer displays information about the principal actors in the event (i.e. `user.name`, `host.name`, `process.name`), and the Netflow row renderer displays information about the directionality, source / destination, protocol, etc. ### Fields in a Network Disconnect Received event `user.name` \ `user.domain` @ `host.name` disconnected via `process.name` `(process.pid)` ## Security (Authentication) User Logon events Endgame Security (Authentication) User Logon events with the following event type and subtype will be rendered in the Timeline via row renderers: ``` endgame.event_type_full: security_event and endgame.event_subtype_full: user_logon ``` ### Runtime matching criteria Security (Authentication) User Logon events matching the following criteria will be rendered: ``` event.category: authentication and event.action: user_logon ``` ### Sample rendered Security (Authentication) User Logon event ![user-logon](https://user-images.githubusercontent.com/4459398/66859339-525cad80-ef48-11e9-851c-c08c302df0fc.png) `SYSTEM` \ `NT AUTHORITY` @ `HD-v1s-d2118419` successfully logged in using logon type `5 - Service` (target logon ID `0x3e7`) via `C:\Windows\System32\services.exe` (`432`) as requested by subject `WIN-Q3DOP1UKA81$` \ `WORKGROUP` (source logon ID `0x3e7`) [![windows-logo](https://user-images.githubusercontent.com/4459398/66249835-e3d15180-e6f6-11e9-89c3-5517c5ed1596.png) `4624`] ### Fields in an Security (Authentication) User Logon event `user.name` \ `user.domain` @ `host.name` successfully logged in using logon type `endgame.logon_type` (target logon ID `endgame.target_logon_id`) via `process.name | process.executable` (`process.pid`) as requested by subject `endgame.subject_user_name` \ `endgame.subject_domain_name` (subject logon ID `endgame.subject_logon_id`) [![windows-logo](https://user-images.githubusercontent.com/4459398/66249835-e3d15180-e6f6-11e9-89c3-5517c5ed1596.png) `event.code | winlog.event_id`] ### Reference: LogonType Enumerations The following enumerated values will humanize the numeric `endgame.logon_type` field: ``` 2 - Interactive 3 - Network 4 - Batch 5 - Service 7 - Unlock 8 - Network Cleartext 9 - New Credentials 10 - Remote Interactive 11 - Cached Interactive ``` ## Security (Authentication) Admin Logon events Endgame Security (Authentication) Admin Logon events with the following event type and subtype will be rendered in the Timeline via row renderers: ``` endgame.event_type_full: security_event and endgame.event_subtype_full: admin_logon ``` ### Runtime matching criteria Security (Authentication) Admin Logon events matching the following criteria will be rendered: ``` event.category: authentication and event.action: admin_logon ``` ### Sample rendered Security (Authentication) Admin Logon event ![admin-logon](https://user-images.githubusercontent.com/4459398/66860598-bc765200-ef4a-11e9-9e58-a96c2b97f4e1.png) With special privileges, `SYSTEM` \ `NT AUTHORITY` @ `HD-v1s-d2118419` successfully logged in via `C:\Windows\System32\services.exe` (`964`) as requested by subject `SYSTEM` \ `NT AUTHORITY` (subject logon ID `0x3e7`) [![windows-logo](https://user-images.githubusercontent.com/4459398/66249835-e3d15180-e6f6-11e9-89c3-5517c5ed1596.png) `4672`] ### Fields in a Security (Authentication) Admin Logon event With special privileges, `user.name` \ `user.domain` @ `host.name` successfully logged in via `process.name | process.executable` (`process.pid`) as requested by subject `endgame.subject_user_name` \ `endgame.subject_domain_name` (subject logon ID `endgame.subject_logon_id`) [![windows-logo](https://user-images.githubusercontent.com/4459398/66249835-e3d15180-e6f6-11e9-89c3-5517c5ed1596.png) `event.code | winlog.event_id`] ## Security (Authentication) Explicit User Logon events Endgame Security (Authentication) Explicit User Logon events with the following event type and subtype will be rendered in the Timeline via row renderers: ``` endgame.event_type_full: security_event and endgame.event_subtype_full: explicit_user_logon ``` ### Runtime matching criteria Security (Authentication) Explicit User Logon events matching the following criteria will be rendered: ``` event.category: authentication and event.action: explicit_user_logon ``` ### Sample rendered Security (Authentication) Explicit User Logon event ![explicit-user-logon](https://user-images.githubusercontent.com/4459398/66860797-170fae00-ef4b-11e9-88c5-befd3dcab070.png) A login was attempted using explicit credentials `Arun` \ `Anvi-Acer` to `HD-v1s-d2118419` via `C:\Windows\System32\services.exe` (`1736`) as requested by subject `ANVI-ACER$` \ `WORKGROUP` (subject logon ID `0x3e7`) [![windows-logo](https://user-images.githubusercontent.com/4459398/66249835-e3d15180-e6f6-11e9-89c3-5517c5ed1596.png) `4648`] ### Fields in an Security (Authentication) Explicit User Logon event A login was attempted using explicit credentials `endgame.target_user_name` \ `endgame.target_domain_name` to `host.name` via `process.name | process.executable` (`process.pid`) as requested by subject `endgame.subject_user_name` \ `endgame.subject_domain_name` (subject logon ID `endgame.subject_logon_id`) [![windows-logo](https://user-images.githubusercontent.com/4459398/66249835-e3d15180-e6f6-11e9-89c3-5517c5ed1596.png) `event.code | winlog.event_id`] ## Security (Authentication) User Logoff events Endgame Security (Authentication) User Logoff events with the following event type and subtype will be rendered in the Timeline via row renderers: ``` endgame.event_type_full: security_event and endgame.event_subtype_full: user_logoff ``` ### Runtime matching criteria Security (Authentication) User Logoff events matching the following criteria will be rendered: ``` event.category: authentication and event.action: user_logoff ``` ### Sample rendered Security (Authentication) User Logoff event ![user-logoff](https://user-images.githubusercontent.com/4459398/66861089-9a310400-ef4b-11e9-9f71-b148409c75a7.png) `Arun` \ `Anvi-Acer` @ `HD-55b-3ec87f66` logged off using logon type `2 - Interactive` (target logon ID `0x16db41e`) via `C:\Windows\System32\services.exe` (`964`) [![windows-logo](https://user-images.githubusercontent.com/4459398/66249835-e3d15180-e6f6-11e9-89c3-5517c5ed1596.png) `4634` ] ### Fields in Security (Authentication) User Logoff event `endgame.target_user_name` \ `endgame.target_domain_name` @ `host.name` logged off using logon type `endgame.logon_type` (target logon ID `endgame.target_logon_id`) via `process.name | process.executable` (`process.pid`) [![windows-logo](https://user-images.githubusercontent.com/4459398/66249835-e3d15180-e6f6-11e9-89c3-5517c5ed1596.png) `event.code | winlog.event_id`] ## Process Creation events Endgame Process Creation events with the following event type and subtype will be rendered in the Timeline via row renderers: ``` endgame.event_type_full: process_event and endgame.event_subtype_full: creation_event ``` ### Runtime matching criteria Process Creation events matching the following criteria will be rendered: ``` event.category: process and event.action: creation_event ``` ### Sample rendered Process Creation event ![creation-event](https://user-images.githubusercontent.com/4459398/66861295-fbf16e00-ef4b-11e9-9455-8a1f13463974.png) `Arun` \ `Anvi-Acer` @ `HD-obe-8bf77f54` started process `Microsoft.Photos.exe` (`441684`) `-ServerName:App.AppXzst44mncqdg84v7sv6p7yznqwssy6f7f.mca` via parent process `svchost.exe` (`8`) `sha256 d4c97ed46046893141652e2ec0056a698f6445109949d7fcabbce331146889ee` `sha1 12563599116157778a22600d2a163d8112aed845` `md5 62d06d7235b37895b68de56687895743` ### Fields in a Process Creation event The following fields will be used to render a Process Creation event: `user.name` \ `user.domain` @ `host.name` started process `process.name` (`process.pid`) `process.args` via parent process `endgame.parent_process_name` (`process.ppid`) `process.hash.sha256` `process.hash.sha1` `process.hash.md5` ## Process Termination events Endgame Process Termination events with the following event type and subtype will be rendered in the Timeline via row renderers: ``` endgame.event_type_full: process_event and endgame.event_subtype_full: termination_event ``` ### Runtime matching criteria Process Termination events matching the following criteria will be rendered: ``` event.category: process and event.action: termination_event ``` ### Sample rendered Process Termination event ![termination-event](https://user-images.githubusercontent.com/4459398/66861495-57bbf700-ef4c-11e9-8e6e-923e9c6bab3e.png) `Arun` \ `Anvi-Acer` @ `HD-obe-8bf77f54` terminated process `RuntimeBroker.exe` (`442384`) with exit code `0` `sha256 87976f3430cc99bc939e0694247c0759961a49832b87218f4313d6fc0bc3a776` `sha1 797255e72d5ed5c058d4785950eba7abaa057653` `md5 bd4401441a21bf1abce6404f4231db4d` ### Fields in a Process Termination event The following fields will be used to render a Process Termination event: `user.name` \ `user.domain` @ `host.name` terminated process `process.name` (`process.pid`) with exit code `endgame.exit_code` `process.hash.sha256` `process.hash.sha1` `process.hash.md5` ## Testing Desk tested in: * Dark / light mode * Chrome `77.0.3865.90` * Firefox `69.0.3` * Safari `13.0.1` * NOT tested in IE11 (due to current blocker) https://github.com/elastic/ecs-dev/issues/178 --- .../__snapshots__/args.test.tsx.snap | 55 +- .../process_draggable.test.tsx.snap | 2 + .../user_host_working_dir.test.tsx.snap | 1 + .../timeline/body/renderers/args.test.tsx | 51 +- .../timeline/body/renderers/args.tsx | 52 +- .../renderers/auditd/generic_details.test.tsx | 46 +- .../body/renderers/auditd/generic_details.tsx | 9 +- .../auditd/generic_file_details.test.tsx | 46 +- .../renderers/auditd/generic_file_details.tsx | 9 +- .../auditd/generic_row_renderer.test.tsx | 4 +- .../dns/dns_request_event_details.test.tsx | 37 ++ .../dns/dns_request_event_details.tsx | 64 ++ .../dns_request_event_details_line.test.tsx | 383 ++++++++++++ .../dns/dns_request_event_details_line.tsx | 179 ++++++ .../body/renderers/dns/translations.ts | 39 ++ .../endgame_security_event_details.test.tsx | 90 +++ .../endgame_security_event_details.tsx | 81 +++ ...dgame_security_event_details_line.test.tsx | 589 ++++++++++++++++++ .../endgame_security_event_details_line.tsx | 255 ++++++++ .../body/renderers/endgame/helpers.test.tsx | 208 +++++++ .../body/renderers/endgame/helpers.ts | 61 ++ .../body/renderers/endgame/translations.ts | 134 ++++ .../body/renderers/exit_code_draggable.tsx | 47 ++ .../body/renderers/file_draggable.tsx | 93 +++ .../body/renderers/get_row_renderer.test.tsx | 4 +- .../timeline/body/renderers/helpers.test.tsx | 68 +- .../timeline/body/renderers/helpers.tsx | 24 + .../renderers/parent_process_draggable.tsx | 74 +++ .../timeline/body/renderers/process.hash.tsx | 79 +++ .../body/renderers/process_draggable.test.tsx | 168 ++++- .../body/renderers/process_draggable.tsx | 138 ++-- .../renderers/system/generic_details.test.tsx | 56 +- .../body/renderers/system/generic_details.tsx | 9 +- .../system/generic_file_details.test.tsx | 516 ++++++++++++++- .../renderers/system/generic_file_details.tsx | 120 +++- .../system/generic_row_renderer.test.tsx | 4 +- .../renderers/system/generic_row_renderer.tsx | 237 ++++++- .../body/renderers/system/translations.ts | 35 ++ .../timeline/body/renderers/translations.ts | 2 +- .../renderers/user_host_working_dir.test.tsx | 35 +- .../body/renderers/user_host_working_dir.tsx | 43 +- .../siem/public/mock/mock_endgame_ecs_data.ts | 579 +++++++++++++++++ 42 files changed, 4478 insertions(+), 248 deletions(-) create mode 100644 x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/dns/dns_request_event_details.test.tsx create mode 100644 x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/dns/dns_request_event_details.tsx create mode 100644 x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/dns/dns_request_event_details_line.test.tsx create mode 100644 x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/dns/dns_request_event_details_line.tsx create mode 100644 x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/dns/translations.ts create mode 100644 x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/endgame/endgame_security_event_details.test.tsx create mode 100644 x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/endgame/endgame_security_event_details.tsx create mode 100644 x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/endgame/endgame_security_event_details_line.test.tsx create mode 100644 x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/endgame/endgame_security_event_details_line.tsx create mode 100644 x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/endgame/helpers.test.tsx create mode 100644 x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/endgame/helpers.ts create mode 100644 x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/endgame/translations.ts create mode 100644 x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/exit_code_draggable.tsx create mode 100644 x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/file_draggable.tsx create mode 100644 x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/parent_process_draggable.tsx create mode 100644 x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/process.hash.tsx create mode 100644 x-pack/legacy/plugins/siem/public/mock/mock_endgame_ecs_data.ts diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/__snapshots__/args.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/__snapshots__/args.test.tsx.snap index 8b5996b06e4e2..7017ffa6fd9f1 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/__snapshots__/args.test.tsx.snap +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/__snapshots__/args.test.tsx.snap @@ -1,10 +1,53 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Args rendering it renders against shallow snapshot 1`] = ` - + + + + + + + + + + + + + + `; diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/__snapshots__/process_draggable.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/__snapshots__/process_draggable.test.tsx.snap index 807c945e13747..0e1634821fb92 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/__snapshots__/process_draggable.test.tsx.snap +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/__snapshots__/process_draggable.test.tsx.snap @@ -3,6 +3,8 @@ exports[`ProcessDraggable rendering it renders against shallow snapshot 1`] = ` diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/args.test.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/args.test.tsx index 9583fef3a5737..284cd0b49cb58 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/args.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/args.test.tsx @@ -10,7 +10,6 @@ import * as React from 'react'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; import { TestProviders } from '../../../../mock'; -import { getEmptyString } from '../../../empty_value'; import { Args } from './args'; describe('Args', () => { @@ -20,28 +19,60 @@ describe('Args', () => { ); expect(toJson(wrapper)).toMatchSnapshot(); }); - test('it returns null if args is undefined', () => { + test('it returns an empty string when both args and process title are undefined', () => { const wrapper = mountWithIntl( + + ); + expect(wrapper.text()).toEqual(''); + }); + + test('it returns an empty string when both args and process title are null', () => { + const wrapper = mountWithIntl( + + + + ); + expect(wrapper.text()).toEqual(''); + }); + + test('it returns an empty string when args is an empty array, and title is an empty string', () => { + const wrapper = mountWithIntl( + + + + ); + expect(wrapper.text()).toEqual(''); + }); + + test('it returns args when args are provided, and process title is NOT provided', () => { + const wrapper = mountWithIntl( + + ); - expect(wrapper.isEmptyRender()).toBeTruthy(); + expect(wrapper.text()).toEqual('arg1arg2arg3'); }); - test('it returns null if args is null', () => { + test('it returns process title when process title is provided, and args is NOT provided', () => { const wrapper = mountWithIntl( { /> ); - expect(wrapper.isEmptyRender()).toBeTruthy(); + expect(wrapper.text()).toEqual('process-title-1'); }); - test('it returns empty string if args happens to be an empty string', () => { + test('it returns both args and process title, when both are provided', () => { const wrapper = mountWithIntl( ); - expect(wrapper.text()).toEqual(getEmptyString()); + expect(wrapper.text()).toEqual('arg1arg2arg3process-title-1'); }); }); }); diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/args.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/args.tsx index b7c19c3f7639d..aad8ef52feeea 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/args.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/args.tsx @@ -5,30 +5,48 @@ */ import * as React from 'react'; -import { pure } from 'recompose'; import { DraggableBadge } from '../../../draggables'; -import { TokensFlexItem } from './helpers'; +import { isNillEmptyOrNotFinite, TokensFlexItem } from './helpers'; interface Props { - eventId: string; + args: string[] | null | undefined; contextId: string; - args: string | null | undefined; + eventId: string; processTitle: string | null | undefined; } -export const Args = pure(({ eventId, contextId, args, processTitle }) => - args != null ? ( - - - - ) : null -); +export const Args = React.memo(({ args, contextId, eventId, processTitle }) => { + if (isNillEmptyOrNotFinite(args) && isNillEmptyOrNotFinite(processTitle)) { + return null; + } + + return ( + <> + {args != null && + args.map((arg, i) => ( + + + + ))} + + {!isNillEmptyOrNotFinite(processTitle) && ( + + + + )} + + ); +}); Args.displayName = 'Args'; diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/auditd/generic_details.test.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/auditd/generic_details.test.tsx index 3270aca83c4cb..90698cc3bf5c9 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/auditd/generic_details.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/auditd/generic_details.test.tsx @@ -44,7 +44,7 @@ describe('GenericDetails', () => { ); expect(wrapper.text()).toEqual( - 'Sessionalice@zeek-sanfranin/generic-text-123gpgconf--list-dirs agent-socket' + 'Sessionalice@zeek-sanfranin/generic-text-123gpgconf(5402)gpgconf--list-dirsagent-socketgpgconf --list-dirs agent-socket' ); }); @@ -82,13 +82,13 @@ describe('GenericDetails', () => { processExecutable="process-1" processTitle="process-title-1" workingDirectory="working-directory-1" - args="arg1 arg2 arg3" + args={['arg1', 'arg2', 'arg3']} result="success" /> ); expect(wrapper.text()).toEqual( - 'Sessionsession-1username-1@host-1inworking-directory-1generic-text-123process-name-1arg1 arg2 arg3with resultsuccess' + 'Sessionsession-1username-1@host-1inworking-directory-1generic-text-123process-name-1(123)arg1arg2arg3process-title-1with resultsuccess' ); }); @@ -109,13 +109,13 @@ describe('GenericDetails', () => { processExecutable="process-1" processTitle="process-title-1" workingDirectory="working-directory-1" - args="arg1 arg2 arg3" + args={['arg1', 'arg2', 'arg3']} result="success" /> ); expect(wrapper.text()).toEqual( - 'Sessionsession-1username-1@host-1inworking-directory-1generic-text-123process-name-1arg1 arg2 arg3with resultsuccess' + 'Sessionsession-1username-1@host-1inworking-directory-1generic-text-123process-name-1(123)arg1arg2arg3process-title-1with resultsuccess' ); }); @@ -136,13 +136,13 @@ describe('GenericDetails', () => { processExecutable="process-1" processTitle="process-title-1" workingDirectory="working-directory-1" - args="arg1 arg2 arg3" + args={['arg1', 'arg2', 'arg3']} result="success" /> ); expect(wrapper.text()).toEqual( - 'Sessionsession-1username-1@host-1inworking-directory-1generic-text-123process-name-1arg1 arg2 arg3with resultsuccess' + 'Sessionsession-1username-1@host-1inworking-directory-1generic-text-123process-name-1(123)arg1arg2arg3process-title-1with resultsuccess' ); }); @@ -163,13 +163,13 @@ describe('GenericDetails', () => { processExecutable="process-1" processTitle="process-title-1" workingDirectory="working-directory-1" - args="arg1 arg2 arg3" + args={['arg1', 'arg2', 'arg3']} result="success" /> ); expect(wrapper.text()).toEqual( - 'Sessionsession-1username-1@host-1inworking-directory-1generic-text-123process-name-1arg1 arg2 arg3with resultsuccess' + 'Sessionsession-1username-1@host-1inworking-directory-1generic-text-123process-name-1(123)arg1arg2arg3process-title-1with resultsuccess' ); }); @@ -190,13 +190,13 @@ describe('GenericDetails', () => { processExecutable="process-1" processTitle="process-title-1" workingDirectory="working-directory-1" - args="arg1 arg2 arg3" + args={['arg1', 'arg2', 'arg3']} result="success" /> ); expect(wrapper.text()).toEqual( - 'Sessionsession-1username-1@host-1inworking-directory-1generic-text-123process-name-1arg1 arg2 arg3with resultsuccess' + 'Sessionsession-1username-1@host-1inworking-directory-1generic-text-123process-name-1(123)arg1arg2arg3process-title-1with resultsuccess' ); }); @@ -217,13 +217,13 @@ describe('GenericDetails', () => { processExecutable="process-1" processTitle="process-title-1" workingDirectory="working-directory-1" - args="arg1 arg2 arg3" + args={['arg1', 'arg2', 'arg3']} result="success" /> ); expect(wrapper.text()).toEqual( - 'Sessionsession-1[username-2]as[username-3]@host-1inworking-directory-1generic-text-123process-name-1arg1 arg2 arg3with resultsuccess' + 'Sessionsession-1[username-2]as[username-3]@host-1inworking-directory-1generic-text-123process-name-1(123)arg1arg2arg3process-title-1with resultsuccess' ); }); @@ -244,13 +244,13 @@ describe('GenericDetails', () => { processExecutable="process-1" processTitle="process-title-1" workingDirectory="working-directory-1" - args="arg1 arg2 arg3" + args={['arg1', 'arg2', 'arg3']} result="success" /> ); expect(wrapper.text()).toEqual( - 'Sessionsession-1[username-1]as[username-2]@host-1inworking-directory-1generic-text-123process-name-1arg1 arg2 arg3with resultsuccess' + 'Sessionsession-1[username-1]as[username-2]@host-1inworking-directory-1generic-text-123process-name-1(123)arg1arg2arg3process-title-1with resultsuccess' ); }); @@ -271,13 +271,13 @@ describe('GenericDetails', () => { processExecutable="process-1" processTitle="process-title-1" workingDirectory="working-directory-1" - args="arg1 arg2 arg3" + args={['arg1', 'arg2', 'arg3']} result="success" /> ); expect(wrapper.text()).toEqual( - 'Sessionsession-1[username-primary]@host-1inworking-directory-1generic-text-123process-name-1arg1 arg2 arg3with resultsuccess' + 'Sessionsession-1[username-primary]@host-1inworking-directory-1generic-text-123process-name-1(123)arg1arg2arg3process-title-1with resultsuccess' ); }); @@ -298,13 +298,13 @@ describe('GenericDetails', () => { processExecutable="process-1" processTitle="process-title-1" workingDirectory="working-directory-1" - args="arg1 arg2 arg3" + args={['arg1', 'arg2', 'arg3']} result="success" /> ); expect(wrapper.text()).toEqual( - 'Sessionsession-1[username-primary]@host-1inworking-directory-1generic-text-123process-name-1arg1 arg2 arg3with resultsuccess' + 'Sessionsession-1[username-primary]@host-1inworking-directory-1generic-text-123process-name-1(123)arg1arg2arg3process-title-1with resultsuccess' ); }); @@ -408,7 +408,7 @@ describe('GenericDetails', () => { expect(wrapper.text()).toEqual('Sessiongeneric-text-123some-process-name'); }); - test('it returns only session and user name if process title with id is given', () => { + test('it returns session, user name, and process title if process title with id is given', () => { const wrapper = mountWithIntl( { /> ); - expect(wrapper.text()).toEqual('Sessionsome-user-name'); + expect(wrapper.text()).toEqual('Sessionsome-user-namesome-process-title'); }); test('it returns only a working directory if that is all that is given with a id', () => { @@ -465,7 +465,7 @@ describe('GenericDetails', () => { id="hello-i-am-an-id" contextId="contextid-123" text="generic-text-123" - args="arg1 arg2 arg 3" + args={['arg1', 'arg2', 'arg 3']} userName={undefined} secondary={undefined} session={undefined} @@ -480,7 +480,7 @@ describe('GenericDetails', () => { /> ); - expect(wrapper.text()).toEqual('Sessionarg1 arg2 arg 3'); + expect(wrapper.text()).toEqual('Sessionarg1arg2arg 3'); }); }); }); diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/auditd/generic_details.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/auditd/generic_details.tsx index f6fa3ea63de0b..127d88a2a2c6d 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/auditd/generic_details.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/auditd/generic_details.tsx @@ -34,7 +34,7 @@ interface Props { processExecutable: string | null | undefined; processTitle: string | null | undefined; workingDirectory: string | null | undefined; - args: string | null | undefined; + args: string[] | null | undefined; session: string | null | undefined; } @@ -56,7 +56,7 @@ export const AuditdGenericLine = pure( session, text, }) => ( - + ( ( const workingDirectory: string | null | undefined = get('process.working_directory[0]', data); const primary: string | null | undefined = get('auditd.summary.actor.primary[0]', data); const secondary: string | null | undefined = get('auditd.summary.actor.secondary[0]', data); - const rawArgs: string[] | null | undefined = get('process.args', data); - const args: string | null = rawArgs != null ? rawArgs.slice(1).join(' ') : null; + const args: string[] | null | undefined = get('process.args', data); if (data.process != null) { return (
diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/auditd/generic_file_details.test.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/auditd/generic_file_details.test.tsx index 629f58ee78183..7630202293f8f 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/auditd/generic_file_details.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/auditd/generic_file_details.test.tsx @@ -48,7 +48,7 @@ describe('GenericFileDetails', () => { ); expect(wrapper.text()).toEqual( - 'Sessionalice@zeek-sanfranin/generic-text-123usinggpgconf--list-dirs agent-socket' + 'Sessionalice@zeek-sanfranin/generic-text-123usinggpgconf(5402)gpgconf--list-dirsagent-socketgpgconf --list-dirs agent-socket' ); }); @@ -87,7 +87,7 @@ describe('GenericFileDetails', () => { processExecutable="process-1" processTitle="process-title-1" workingDirectory="working-directory-1" - args="arg1 arg2 arg3" + args={['arg1', 'arg2', 'arg3']} filePath="/somepath" fileIcon="document" result="success" @@ -95,7 +95,7 @@ describe('GenericFileDetails', () => { ); expect(wrapper.text()).toEqual( - 'Sessionsession-1username-1@host-1inworking-directory-1generic-text-123/somepathusingprocess-name-1arg1 arg2 arg3with resultsuccess' + 'Sessionsession-1username-1@host-1inworking-directory-1generic-text-123/somepathusingprocess-name-1(123)arg1arg2arg3process-title-1with resultsuccess' ); }); @@ -116,7 +116,7 @@ describe('GenericFileDetails', () => { processExecutable="process-1" processTitle="process-title-1" workingDirectory="working-directory-1" - args="arg1 arg2 arg3" + args={['arg1', 'arg2', 'arg3']} filePath="/somepath" fileIcon="document" result="success" @@ -124,7 +124,7 @@ describe('GenericFileDetails', () => { ); expect(wrapper.text()).toEqual( - 'Sessionsession-1username-1@host-1inworking-directory-1generic-text-123/somepathusingprocess-name-1arg1 arg2 arg3with resultsuccess' + 'Sessionsession-1username-1@host-1inworking-directory-1generic-text-123/somepathusingprocess-name-1(123)arg1arg2arg3process-title-1with resultsuccess' ); }); @@ -145,7 +145,7 @@ describe('GenericFileDetails', () => { processExecutable="process-1" processTitle="process-title-1" workingDirectory="working-directory-1" - args="arg1 arg2 arg3" + args={['arg1', 'arg2', 'arg3']} filePath="/somepath" fileIcon="document" result="success" @@ -153,7 +153,7 @@ describe('GenericFileDetails', () => { ); expect(wrapper.text()).toEqual( - 'Sessionsession-1username-1@host-1inworking-directory-1generic-text-123/somepathusingprocess-name-1arg1 arg2 arg3with resultsuccess' + 'Sessionsession-1username-1@host-1inworking-directory-1generic-text-123/somepathusingprocess-name-1(123)arg1arg2arg3process-title-1with resultsuccess' ); }); @@ -174,7 +174,7 @@ describe('GenericFileDetails', () => { processExecutable="process-1" processTitle="process-title-1" workingDirectory="working-directory-1" - args="arg1 arg2 arg3" + args={['arg1', 'arg2', 'arg3']} filePath="/somepath" fileIcon="document" result="success" @@ -182,7 +182,7 @@ describe('GenericFileDetails', () => { ); expect(wrapper.text()).toEqual( - 'Sessionsession-1username-1@host-1inworking-directory-1generic-text-123/somepathusingprocess-name-1arg1 arg2 arg3with resultsuccess' + 'Sessionsession-1username-1@host-1inworking-directory-1generic-text-123/somepathusingprocess-name-1(123)arg1arg2arg3process-title-1with resultsuccess' ); }); @@ -203,7 +203,7 @@ describe('GenericFileDetails', () => { processExecutable="process-1" processTitle="process-title-1" workingDirectory="working-directory-1" - args="arg1 arg2 arg3" + args={['arg1', 'arg2', 'arg3']} filePath="/somepath" fileIcon="document" result="success" @@ -211,7 +211,7 @@ describe('GenericFileDetails', () => { ); expect(wrapper.text()).toEqual( - 'Sessionsession-1username-1@host-1inworking-directory-1generic-text-123/somepathusingprocess-name-1arg1 arg2 arg3with resultsuccess' + 'Sessionsession-1username-1@host-1inworking-directory-1generic-text-123/somepathusingprocess-name-1(123)arg1arg2arg3process-title-1with resultsuccess' ); }); @@ -232,7 +232,7 @@ describe('GenericFileDetails', () => { processExecutable="process-1" processTitle="process-title-1" workingDirectory="working-directory-1" - args="arg1 arg2 arg3" + args={['arg1', 'arg2', 'arg3']} filePath="/somepath" fileIcon="document" result="success" @@ -240,7 +240,7 @@ describe('GenericFileDetails', () => { ); expect(wrapper.text()).toEqual( - 'Sessionsession-1[username-2]as[username-3]@host-1inworking-directory-1generic-text-123/somepathusingprocess-name-1arg1 arg2 arg3with resultsuccess' + 'Sessionsession-1[username-2]as[username-3]@host-1inworking-directory-1generic-text-123/somepathusingprocess-name-1(123)arg1arg2arg3process-title-1with resultsuccess' ); }); @@ -261,7 +261,7 @@ describe('GenericFileDetails', () => { processExecutable="process-1" processTitle="process-title-1" workingDirectory="working-directory-1" - args="arg1 arg2 arg3" + args={['arg1', 'arg2', 'arg3']} filePath="/somepath" fileIcon="document" result="success" @@ -269,7 +269,7 @@ describe('GenericFileDetails', () => { ); expect(wrapper.text()).toEqual( - 'Sessionsession-1[username-1]as[username-2]@host-1inworking-directory-1generic-text-123/somepathusingprocess-name-1arg1 arg2 arg3with resultsuccess' + 'Sessionsession-1[username-1]as[username-2]@host-1inworking-directory-1generic-text-123/somepathusingprocess-name-1(123)arg1arg2arg3process-title-1with resultsuccess' ); }); @@ -290,7 +290,7 @@ describe('GenericFileDetails', () => { processExecutable="process-1" processTitle="process-title-1" workingDirectory="working-directory-1" - args="arg1 arg2 arg3" + args={['arg1', 'arg2', 'arg3']} filePath="/somepath" fileIcon="document" result="success" @@ -298,7 +298,7 @@ describe('GenericFileDetails', () => { ); expect(wrapper.text()).toEqual( - 'Sessionsession-1[username-primary]@host-1inworking-directory-1generic-text-123/somepathusingprocess-name-1arg1 arg2 arg3with resultsuccess' + 'Sessionsession-1[username-primary]@host-1inworking-directory-1generic-text-123/somepathusingprocess-name-1(123)arg1arg2arg3process-title-1with resultsuccess' ); }); @@ -319,7 +319,7 @@ describe('GenericFileDetails', () => { processExecutable="process-1" processTitle="process-title-1" workingDirectory="working-directory-1" - args="arg1 arg2 arg3" + args={['arg1', 'arg2', 'arg3']} filePath="/somepath" fileIcon="document" result="success" @@ -327,7 +327,7 @@ describe('GenericFileDetails', () => { ); expect(wrapper.text()).toEqual( - 'Sessionsession-1[username-primary]@host-1inworking-directory-1generic-text-123/somepathusingprocess-name-1arg1 arg2 arg3with resultsuccess' + 'Sessionsession-1[username-primary]@host-1inworking-directory-1generic-text-123/somepathusingprocess-name-1(123)arg1arg2arg3process-title-1with resultsuccess' ); }); @@ -439,7 +439,7 @@ describe('GenericFileDetails', () => { expect(wrapper.text()).toEqual('Sessiongeneric-text-123usingsome-process-name'); }); - test('it returns only session and user name if process title with id is given', () => { + test('it returns session user name and title if process title with id is given', () => { const wrapper = mountWithIntl( { /> ); - expect(wrapper.text()).toEqual('Sessionsome-user-name'); + expect(wrapper.text()).toEqual('Sessionsome-user-namesome-process-title'); }); test('it returns only a working directory if that is all that is given with a id', () => { @@ -500,7 +500,7 @@ describe('GenericFileDetails', () => { id="hello-i-am-an-id" contextId="contextid-123" text="generic-text-123" - args="arg1 arg2 arg 3" + args={['arg1', 'arg2', 'arg 3']} fileIcon="document" userName={undefined} secondary={undefined} @@ -517,7 +517,7 @@ describe('GenericFileDetails', () => { /> ); - expect(wrapper.text()).toEqual('Sessionarg1 arg2 arg 3'); + expect(wrapper.text()).toEqual('Sessionarg1arg2arg 3'); }); }); }); diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/auditd/generic_file_details.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/auditd/generic_file_details.tsx index 318a3abe9c00f..076c605ecb89f 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/auditd/generic_file_details.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/auditd/generic_file_details.tsx @@ -36,7 +36,7 @@ interface Props { processExecutable: string | null | undefined; processTitle: string | null | undefined; workingDirectory: string | null | undefined; - args: string | null | undefined; + args: string[] | null | undefined; session: string | null | undefined; } @@ -60,7 +60,7 @@ export const AuditdGenericFileLine = pure( text, fileIcon, }) => ( - + ( ( const filePath: string | null | undefined = get('file.path[0]', data); const primary: string | null | undefined = get('auditd.summary.actor.primary[0]', data); const secondary: string | null | undefined = get('auditd.summary.actor.secondary[0]', data); - const rawArgs: string[] | null | undefined = get('process.args', data); - const args: string | null = rawArgs != null ? rawArgs.slice(1).join(' ') : null; + const args: string[] | null | undefined = get('process.args', data); if (data.process != null) { return ( diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/auditd/generic_row_renderer.test.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/auditd/generic_row_renderer.test.tsx index cb2f14eeaa265..cf5f57f031da7 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/auditd/generic_row_renderer.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/auditd/generic_row_renderer.test.tsx @@ -92,7 +92,7 @@ describe('GenericRowRenderer', () => { ); expect(wrapper.text()).toContain( - 'some children Session246alice@zeek-londonsome textwgetwith resultsuccessDestination93.184.216.34:80' + 'some children Session246alice@zeek-londonsome textwget(1490)wget www.example.comwith resultsuccessDestination93.184.216.34:80' ); }); }); @@ -171,7 +171,7 @@ describe('GenericRowRenderer', () => { ); expect(wrapper.text()).toContain( - 'some children Sessionunsetroot@zeek-londonin/some text/proc/15990/attr/currentusingsystemd-journalwith resultsuccess' + 'some children Sessionunsetroot@zeek-londonin/some text/proc/15990/attr/currentusingsystemd-journal(27244)/lib/systemd/systemd-journaldwith resultsuccess' ); }); }); diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/dns/dns_request_event_details.test.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/dns/dns_request_event_details.test.tsx new file mode 100644 index 0000000000000..805888afd2964 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/dns/dns_request_event_details.test.tsx @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as React from 'react'; +import { mountWithIntl } from 'test_utils/enzyme_helpers'; + +import { TestProviders } from '../../../../../mock'; +import { mockBrowserFields } from '../../../../../../public/containers/source/mock'; +import { mockEndgameDnsRequest } from '../../../../../../public/mock/mock_endgame_ecs_data'; + +import { DnsRequestEventDetails } from './dns_request_event_details'; + +describe('DnsRequestEventDetails', () => { + test('it renders the expected text given an Endgame DNS request_event', () => { + const wrapper = mountWithIntl( + + + + ); + expect(wrapper.text()).toEqual( + 'SYSTEM\\NT AUTHORITY@HD-obe-8bf77f54asked forupdate.googleapis.comwith question typeA, which resolved to10.100.197.67viaGoogleUpdate.exe(443192)3008dns' + ); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/dns/dns_request_event_details.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/dns/dns_request_event_details.tsx new file mode 100644 index 0000000000000..752163901de2e --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/dns/dns_request_event_details.tsx @@ -0,0 +1,64 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EuiSpacer } from '@elastic/eui'; +import { get } from 'lodash/fp'; +import * as React from 'react'; + +import { BrowserFields } from '../../../../../containers/source'; +import { Details } from '../helpers'; +import { Ecs } from '../../../../../graphql/types'; +import { NetflowRenderer } from '../netflow'; + +import { DnsRequestEventDetailsLine } from './dns_request_event_details_line'; + +interface Props { + browserFields: BrowserFields; + contextId: string; + data: Ecs; + timelineId: string; +} + +export const DnsRequestEventDetails = React.memo(({ data, contextId, timelineId }) => { + const dnsQuestionName: string | null | undefined = get('dns.question.name[0]', data); + const dnsQuestionType: string | null | undefined = get('dns.question.type[0]', data); + const dnsResolvedIp: string | null | undefined = get('dns.resolved_ip[0]', data); + const dnsResponseCode: string | null | undefined = get('dns.response_code[0]', data); + const eventCode: string | null | undefined = get('event.code[0]', data); + const hostName: string | null | undefined = get('host.name[0]', data); + const id = data._id; + const processExecutable: string | null | undefined = get('process.executable[0]', data); + const processName: string | null | undefined = get('process.name[0]', data); + const processPid: number | null | undefined = get('process.pid[0]', data); + const userDomain: string | null | undefined = get('user.domain[0]', data); + const userName: string | null | undefined = get('user.name[0]', data); + const winlogEventId: string | null | undefined = get('winlog.event_id[0]', data); + + return ( +
+ + + +
+ ); +}); + +DnsRequestEventDetails.displayName = 'DnsRequestEventDetails'; diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/dns/dns_request_event_details_line.test.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/dns/dns_request_event_details_line.test.tsx new file mode 100644 index 0000000000000..4c45846574d7c --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/dns/dns_request_event_details_line.test.tsx @@ -0,0 +1,383 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as React from 'react'; +import { mountWithIntl } from 'test_utils/enzyme_helpers'; + +import { TestProviders } from '../../../../../mock'; + +import { DnsRequestEventDetailsLine } from './dns_request_event_details_line'; + +describe('DnsRequestEventDetailsLine', () => { + test('it renders the expected text when all properties are provided', () => { + const wrapper = mountWithIntl( + + + + ); + expect(wrapper.text()).toEqual( + '[userName]\\[userDomain]@[hostName]asked for[dnsQuestionName]with question type[dnsQuestionType], which resolved to[dnsResolvedIp](response code:[dnsResponseCode])via[processName](123)[eventCode]' + ); + }); + + test('it renders the expected text when dnsQuestionName is NOT provided', () => { + const wrapper = mountWithIntl( + + + + ); + expect(wrapper.text()).toEqual( + '[userName]\\[userDomain]@[hostName]with question type[dnsQuestionType], which resolved to[dnsResolvedIp](response code:[dnsResponseCode])via[processName](123)[eventCode]' + ); + }); + + test('it renders the expected text when dnsQuestionType is NOT provided', () => { + const wrapper = mountWithIntl( + + + + ); + expect(wrapper.text()).toEqual( + '[userName]\\[userDomain]@[hostName]asked for[dnsQuestionName], which resolved to[dnsResolvedIp](response code:[dnsResponseCode])via[processName](123)[eventCode]' + ); + }); + + test('it renders the expected text when dnsResolvedIp is NOT provided', () => { + const wrapper = mountWithIntl( + + + + ); + expect(wrapper.text()).toEqual( + '[userName]\\[userDomain]@[hostName]asked for[dnsQuestionName]with question type[dnsQuestionType](response code:[dnsResponseCode])via[processName](123)[eventCode]' + ); + }); + + test('it renders the expected text when dnsResponseCode is NOT provided', () => { + const wrapper = mountWithIntl( + + + + ); + expect(wrapper.text()).toEqual( + '[userName]\\[userDomain]@[hostName]asked for[dnsQuestionName]with question type[dnsQuestionType], which resolved to[dnsResolvedIp]via[processName](123)[eventCode]' + ); + }); + + test('it renders the expected text when eventCode is NOT provided', () => { + const wrapper = mountWithIntl( + + + + ); + expect(wrapper.text()).toEqual( + '[userName]\\[userDomain]@[hostName]asked for[dnsQuestionName]with question type[dnsQuestionType], which resolved to[dnsResolvedIp](response code:[dnsResponseCode])via[processName](123)[winlogEventId]' + ); + }); + + test('it renders the expected text when hostName is NOT provided', () => { + const wrapper = mountWithIntl( + + + + ); + expect(wrapper.text()).toEqual( + '[userName]\\[userDomain]asked for[dnsQuestionName]with question type[dnsQuestionType], which resolved to[dnsResolvedIp](response code:[dnsResponseCode])via[processName](123)[eventCode]' + ); + }); + + test('it renders the expected text when processExecutable is NOT provided', () => { + const wrapper = mountWithIntl( + + + + ); + expect(wrapper.text()).toEqual( + '[userName]\\[userDomain]@[hostName]asked for[dnsQuestionName]with question type[dnsQuestionType], which resolved to[dnsResolvedIp](response code:[dnsResponseCode])via[processName](123)[eventCode]' + ); + }); + + test('it renders the expected text when processName is NOT provided', () => { + const wrapper = mountWithIntl( + + + + ); + expect(wrapper.text()).toEqual( + '[userName]\\[userDomain]@[hostName]asked for[dnsQuestionName]with question type[dnsQuestionType], which resolved to[dnsResolvedIp](response code:[dnsResponseCode])via[processExecutable](123)[eventCode]' + ); + }); + + test('it renders the expected text when processPid is NOT provided', () => { + const wrapper = mountWithIntl( + + + + ); + expect(wrapper.text()).toEqual( + '[userName]\\[userDomain]@[hostName]asked for[dnsQuestionName]with question type[dnsQuestionType], which resolved to[dnsResolvedIp](response code:[dnsResponseCode])via[processName][eventCode]' + ); + }); + + test('it renders the expected text when userDomain is NOT provided', () => { + const wrapper = mountWithIntl( + + + + ); + expect(wrapper.text()).toEqual( + '[userName]@[hostName]asked for[dnsQuestionName]with question type[dnsQuestionType], which resolved to[dnsResolvedIp](response code:[dnsResponseCode])via[processName](123)[eventCode]' + ); + }); + + test('it renders the expected text when userName is NOT provided', () => { + const wrapper = mountWithIntl( + + + + ); + expect(wrapper.text()).toEqual( + '\\[userDomain][hostName]asked for[dnsQuestionName]with question type[dnsQuestionType], which resolved to[dnsResolvedIp](response code:[dnsResponseCode])via[processName](123)[eventCode]' + ); + }); + + test('it renders the expected text when winlogEventId is NOT provided', () => { + const wrapper = mountWithIntl( + + + + ); + expect(wrapper.text()).toEqual( + '[userName]\\[userDomain]@[hostName]asked for[dnsQuestionName]with question type[dnsQuestionType], which resolved to[dnsResolvedIp](response code:[dnsResponseCode])via[processName](123)[eventCode]' + ); + }); + + test('it renders the expected text when both eventCode and winlogEventId are NOT provided', () => { + const wrapper = mountWithIntl( + + + + ); + expect(wrapper.text()).toEqual( + '[userName]\\[userDomain]@[hostName]asked for[dnsQuestionName]with question type[dnsQuestionType], which resolved to[dnsResolvedIp](response code:[dnsResponseCode])via[processName](123)' + ); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/dns/dns_request_event_details_line.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/dns/dns_request_event_details_line.tsx new file mode 100644 index 0000000000000..fd49395379e24 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/dns/dns_request_event_details_line.tsx @@ -0,0 +1,179 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EuiFlexGroup } from '@elastic/eui'; +import * as React from 'react'; + +import { DraggableBadge } from '../../../../draggables'; +import { isNillEmptyOrNotFinite, TokensFlexItem } from '../helpers'; +import { ProcessDraggableWithNonExistentProcess } from '../process_draggable'; +import { UserHostWorkingDir } from '../user_host_working_dir'; + +import * as i18n from './translations'; + +interface Props { + contextId: string; + dnsQuestionName: string | null | undefined; + dnsQuestionType: string | null | undefined; + dnsResolvedIp: string | null | undefined; + dnsResponseCode: string | null | undefined; + eventCode: string | null | undefined; + hostName: string | null | undefined; + id: string; + processExecutable: string | null | undefined; + processName: string | null | undefined; + processPid: number | null | undefined; + userDomain: string | null | undefined; + userName: string | null | undefined; + winlogEventId: string | null | undefined; +} + +export const DnsRequestEventDetailsLine = React.memo( + ({ + contextId, + dnsQuestionName, + dnsQuestionType, + dnsResolvedIp, + dnsResponseCode, + eventCode, + hostName, + id, + processExecutable, + processName, + processPid, + userDomain, + userName, + winlogEventId, + }) => { + return ( + <> + + + + {!isNillEmptyOrNotFinite(dnsQuestionName) && ( + <> + + {i18n.ASKED_FOR} + + + + + + )} + + {!isNillEmptyOrNotFinite(dnsQuestionType) && ( + <> + + {i18n.WITH_QUESTION_TYPE} + + + + + + )} + + {!isNillEmptyOrNotFinite(dnsResolvedIp) && ( + <> + + {i18n.WHICH_RESOLVED_TO} + + + + + + )} + + {!isNillEmptyOrNotFinite(dnsResponseCode) && ( + <> + + {'('} + + + {i18n.RESPONSE_CODE} + + + + + + {')'} + + + )} + + + {i18n.VIA} + + + + + + + {(!isNillEmptyOrNotFinite(eventCode) || !isNillEmptyOrNotFinite(winlogEventId)) && ( + <> + {!isNillEmptyOrNotFinite(eventCode) ? ( + + + + ) : ( + + + + )} + + )} + + + ); + } +); + +DnsRequestEventDetailsLine.displayName = 'DnsRequestEventDetailsLine'; diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/dns/translations.ts b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/dns/translations.ts new file mode 100644 index 0000000000000..b86b88370cdf5 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/dns/translations.ts @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; + +export const ASKED_FOR = i18n.translate( + 'xpack.siem.timeline.body.renderers.dns.askedForDescription', + { + defaultMessage: 'asked for', + } +); + +export const RESPONSE_CODE = i18n.translate( + 'xpack.siem.timeline.body.renderers.dns.responseCodeDescription', + { + defaultMessage: 'response code:', + } +); + +export const VIA = i18n.translate('xpack.siem.timeline.body.renderers.dns.viaDescription', { + defaultMessage: 'via', +}); + +export const WHICH_RESOLVED_TO = i18n.translate( + 'xpack.siem.timeline.body.renderers.dns.whichResolvedToDescription', + { + defaultMessage: ', which resolved to', + } +); + +export const WITH_QUESTION_TYPE = i18n.translate( + 'xpack.siem.timeline.body.renderers.dns.withQuestionTypeDescription', + { + defaultMessage: 'with question type', + } +); diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/endgame/endgame_security_event_details.test.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/endgame/endgame_security_event_details.test.tsx new file mode 100644 index 0000000000000..e1da17ad2904b --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/endgame/endgame_security_event_details.test.tsx @@ -0,0 +1,90 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as React from 'react'; +import { mountWithIntl } from 'test_utils/enzyme_helpers'; + +import { TestProviders } from '../../../../../mock'; +import { mockBrowserFields } from '../../../../../../public/containers/source/mock'; +import { + mockEndgameAdminLogon, + mockEndgameExplicitUserLogon, + mockEndgameUserLogon, + mockEndgameUserLogoff, +} from '../../../../../../public/mock/mock_endgame_ecs_data'; + +import { EndgameSecurityEventDetails } from './endgame_security_event_details'; + +describe('EndgameSecurityEventDetails', () => { + test('it renders the expected text given an Endgame Security user_logon event', () => { + const wrapper = mountWithIntl( + + + + ); + expect(wrapper.text()).toEqual( + 'SYSTEM\\NT AUTHORITY@HD-v1s-d2118419successfully logged inusing logon type5 - Service(target logon ID0x3e7)viaC:\\Windows\\System32\\services.exe(432)as requested by subjectWIN-Q3DOP1UKA81$(subject logon ID0x3e7)4624' + ); + }); + + test('it renders the expected text given an Endgame Security admin_logon event', () => { + const wrapper = mountWithIntl( + + + + ); + expect(wrapper.text()).toEqual( + 'With special privileges,SYSTEM\\NT AUTHORITY@HD-obe-8bf77f54successfully logged inviaC:\\Windows\\System32\\lsass.exe(964)as requested by subjectSYSTEM\\NT AUTHORITY4672' + ); + }); + + test('it renders the expected text given an Endgame Security explicit_user_logon event', () => { + const wrapper = mountWithIntl( + + + + ); + expect(wrapper.text()).toEqual( + 'A login was attempted using explicit credentialsArun\\Anvi-AcertoHD-55b-3ec87f66viaC:\\Windows\\System32\\svchost.exe(1736)as requested by subjectANVI-ACER$\\WORKGROUP(subject logon ID0x3e7)4648' + ); + }); + + test('it renders the expected text given an Endgame Security user_logoff event', () => { + const wrapper = mountWithIntl( + + + + ); + expect(wrapper.text()).toEqual( + 'Arun\\Anvi-Acer@HD-55b-3ec87f66logged offusing logon type2 - Interactive(target logon ID0x16db41e)viaC:\\Windows\\System32\\lsass.exe(964)4634' + ); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/endgame/endgame_security_event_details.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/endgame/endgame_security_event_details.tsx new file mode 100644 index 0000000000000..10f9c4ad9e545 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/endgame/endgame_security_event_details.tsx @@ -0,0 +1,81 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EuiSpacer } from '@elastic/eui'; +import { get } from 'lodash/fp'; +import * as React from 'react'; + +import { BrowserFields } from '../../../../../containers/source'; +import { Ecs } from '../../../../../graphql/types'; +import { NetflowRenderer } from '../netflow'; + +import { EndgameSecurityEventDetailsLine } from './endgame_security_event_details_line'; +import { Details } from '../helpers'; + +interface Props { + browserFields: BrowserFields; + contextId: string; + data: Ecs; + timelineId: string; +} + +export const EndgameSecurityEventDetails = React.memo(({ data, contextId, timelineId }) => { + const endgameLogonType: number | null | undefined = get('endgame.logon_type[0]', data); + const endgameSubjectDomainName: string | null | undefined = get( + 'endgame.subject_domain_name[0]', + data + ); + const endgameSubjectLogonId: string | null | undefined = get('endgame.subject_logon_id[0]', data); + const endgameSubjectUserName: string | null | undefined = get( + 'endgame.subject_user_name[0]', + data + ); + const endgameTargetLogonId: string | null | undefined = get('endgame.target_logon_id[0]', data); + const endgameTargetDomainName: string | null | undefined = get( + 'endgame.target_domain_name[0]', + data + ); + const endgameTargetUserName: string | null | undefined = get('endgame.target_user_name[0]', data); + const eventAction: string | null | undefined = get('event.action[0]', data); + const eventCode: string | null | undefined = get('event.code[0]', data); + const hostName: string | null | undefined = get('host.name[0]', data); + const id = data._id; + const processExecutable: string | null | undefined = get('process.executable[0]', data); + const processName: string | null | undefined = get('process.name[0]', data); + const processPid: number | null | undefined = get('process.pid[0]', data); + const userDomain: string | null | undefined = get('user.domain[0]', data); + const userName: string | null | undefined = get('user.name[0]', data); + const winlogEventId: string | null | undefined = get('winlog.event_id[0]', data); + + return ( +
+ + + +
+ ); +}); + +EndgameSecurityEventDetails.displayName = 'EndgameSecurityEventDetails'; diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/endgame/endgame_security_event_details_line.test.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/endgame/endgame_security_event_details_line.test.tsx new file mode 100644 index 0000000000000..80635e9f63ac7 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/endgame/endgame_security_event_details_line.test.tsx @@ -0,0 +1,589 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as React from 'react'; +import { mountWithIntl } from 'test_utils/enzyme_helpers'; + +import { TestProviders } from '../../../../../mock'; + +import { EndgameSecurityEventDetailsLine } from './endgame_security_event_details_line'; + +describe('EndgameSecurityEventDetailsLine', () => { + test('it renders the expected text when all properties are provided and event action is admin_logon', () => { + const wrapper = mountWithIntl( + + + + ); + expect(wrapper.text()).toEqual( + 'With special privileges,[userName]\\[userDomain]@[hostName]successfully logged inusing logon type2 - Interactive(target logon ID[endgameTargetLogonId])via[processName](123)as requested by subject[endgameSubjectUserName]\\[endgameSubjectDomainName](subject logon ID[endgameSubjectLogonId])[eventCode]' + ); + }); + + test('it renders the expected text when all properties are provided and event action is explicit_user_logon', () => { + const wrapper = mountWithIntl( + + + + ); + expect(wrapper.text()).toEqual( + 'A login was attempted using explicit credentials[endgameTargetUserName]\\[endgameTargetDomainName]to[hostName]using logon type2 - Interactive(target logon ID[endgameTargetLogonId])via[processName](123)as requested by subject[endgameSubjectUserName]\\[endgameSubjectDomainName](subject logon ID[endgameSubjectLogonId])[eventCode]' + ); + }); + + test('it renders the expected text when endgameLogonType is NOT provided', () => { + const wrapper = mountWithIntl( + + + + ); + expect(wrapper.text()).toEqual( + 'A login was attempted using explicit credentials[endgameTargetUserName]\\[endgameTargetDomainName]to[hostName](target logon ID[endgameTargetLogonId])via[processName](123)as requested by subject[endgameSubjectUserName]\\[endgameSubjectDomainName](subject logon ID[endgameSubjectLogonId])[eventCode]' + ); + }); + + test('it renders the expected text when endgameSubjectDomainName is NOT provided', () => { + const wrapper = mountWithIntl( + + + + ); + expect(wrapper.text()).toEqual( + 'A login was attempted using explicit credentials[endgameTargetUserName]\\[endgameTargetDomainName]to[hostName]using logon type2 - Interactive(target logon ID[endgameTargetLogonId])via[processName](123)as requested by subject[endgameSubjectUserName](subject logon ID[endgameSubjectLogonId])[eventCode]' + ); + }); + + test('it renders the expected text when endgameSubjectLogonId is NOT provided', () => { + const wrapper = mountWithIntl( + + + + ); + expect(wrapper.text()).toEqual( + 'A login was attempted using explicit credentials[endgameTargetUserName]\\[endgameTargetDomainName]to[hostName]using logon type2 - Interactive(target logon ID[endgameTargetLogonId])via[processName](123)as requested by subject[endgameSubjectUserName]\\[endgameSubjectDomainName][eventCode]' + ); + }); + + test('it renders the expected text when when endgameSubjectUserName is NOT provided', () => { + const wrapper = mountWithIntl( + + + + ); + expect(wrapper.text()).toEqual( + 'A login was attempted using explicit credentials[endgameTargetUserName]\\[endgameTargetDomainName]to[hostName]using logon type2 - Interactive(target logon ID[endgameTargetLogonId])via[processName](123)\\[endgameSubjectDomainName](subject logon ID[endgameSubjectLogonId])[eventCode]' + ); + }); + + test('it renders the expected text when endgameTargetDomainName is NOT provided', () => { + const wrapper = mountWithIntl( + + + + ); + expect(wrapper.text()).toEqual( + 'A login was attempted using explicit credentials[endgameTargetUserName]to[hostName]using logon type2 - Interactive(target logon ID[endgameTargetLogonId])via[processName](123)as requested by subject[endgameSubjectUserName]\\[endgameSubjectDomainName](subject logon ID[endgameSubjectLogonId])[eventCode]' + ); + }); + + test('it renders the expected text when endgameTargetLogonId is NOT provided', () => { + const wrapper = mountWithIntl( + + + + ); + expect(wrapper.text()).toEqual( + 'A login was attempted using explicit credentials[endgameTargetUserName]\\[endgameTargetDomainName]to[hostName]using logon type2 - Interactivevia[processName](123)as requested by subject[endgameSubjectUserName]\\[endgameSubjectDomainName](subject logon ID[endgameSubjectLogonId])[eventCode]' + ); + }); + + test('it renders the expected text when endgameTargetUserName is NOT provided', () => { + const wrapper = mountWithIntl( + + + + ); + expect(wrapper.text()).toEqual( + 'A login was attempted using explicit credentials\\[endgameTargetDomainName][hostName]using logon type2 - Interactive(target logon ID[endgameTargetLogonId])via[processName](123)as requested by subject[endgameSubjectUserName]\\[endgameSubjectDomainName](subject logon ID[endgameSubjectLogonId])[eventCode]' + ); + }); + + test('it renders the expected text when eventAction is NOT provided', () => { + const wrapper = mountWithIntl( + + + + ); + expect(wrapper.text()).toEqual( + '[userName]\\[userDomain]@[hostName]successfully logged inusing logon type2 - Interactive(target logon ID[endgameTargetLogonId])via[processName](123)as requested by subject[endgameSubjectUserName]\\[endgameSubjectDomainName](subject logon ID[endgameSubjectLogonId])[eventCode]' + ); + }); + + test('it renders the expected text when eventCode is NOT provided', () => { + const wrapper = mountWithIntl( + + + + ); + expect(wrapper.text()).toEqual( + 'A login was attempted using explicit credentials[endgameTargetUserName]\\[endgameTargetDomainName]to[hostName]using logon type2 - Interactive(target logon ID[endgameTargetLogonId])via[processName](123)as requested by subject[endgameSubjectUserName]\\[endgameSubjectDomainName](subject logon ID[endgameSubjectLogonId])[winlogEventId]' + ); + }); + + test('it renders the expected text when hostName is NOT provided', () => { + const wrapper = mountWithIntl( + + + + ); + expect(wrapper.text()).toEqual( + 'A login was attempted using explicit credentials[endgameTargetUserName]\\[endgameTargetDomainName]using logon type2 - Interactive(target logon ID[endgameTargetLogonId])via[processName](123)as requested by subject[endgameSubjectUserName]\\[endgameSubjectDomainName](subject logon ID[endgameSubjectLogonId])[eventCode]' + ); + }); + + test('it renders the expected text when processExecutable is NOT provided', () => { + const wrapper = mountWithIntl( + + + + ); + expect(wrapper.text()).toEqual( + 'A login was attempted using explicit credentials[endgameTargetUserName]\\[endgameTargetDomainName]to[hostName]using logon type2 - Interactive(target logon ID[endgameTargetLogonId])via[processName](123)as requested by subject[endgameSubjectUserName]\\[endgameSubjectDomainName](subject logon ID[endgameSubjectLogonId])[eventCode]' + ); + }); + + test('it renders the expected text when processName is NOT provided', () => { + const wrapper = mountWithIntl( + + + + ); + expect(wrapper.text()).toEqual( + 'A login was attempted using explicit credentials[endgameTargetUserName]\\[endgameTargetDomainName]to[hostName]using logon type2 - Interactive(target logon ID[endgameTargetLogonId])via[processExecutable](123)as requested by subject[endgameSubjectUserName]\\[endgameSubjectDomainName](subject logon ID[endgameSubjectLogonId])[eventCode]' + ); + }); + + test('it renders the expected text when processPid is NOT provided', () => { + const wrapper = mountWithIntl( + + + + ); + expect(wrapper.text()).toEqual( + 'A login was attempted using explicit credentials[endgameTargetUserName]\\[endgameTargetDomainName]to[hostName]using logon type2 - Interactive(target logon ID[endgameTargetLogonId])via[processName]as requested by subject[endgameSubjectUserName]\\[endgameSubjectDomainName](subject logon ID[endgameSubjectLogonId])[eventCode]' + ); + }); + + test('it renders the expected text when userDomain is NOT provided', () => { + const wrapper = mountWithIntl( + + + + ); + expect(wrapper.text()).toEqual( + 'With special privileges,[userName]@[hostName]successfully logged inusing logon type2 - Interactive(target logon ID[endgameTargetLogonId])via[processName](123)as requested by subject[endgameSubjectUserName]\\[endgameSubjectDomainName](subject logon ID[endgameSubjectLogonId])[eventCode]' + ); + }); + + test('it renders the expected text when userName is NOT provided', () => { + const wrapper = mountWithIntl( + + + + ); + expect(wrapper.text()).toEqual( + 'With special privileges,\\[userDomain][hostName]successfully logged inusing logon type2 - Interactive(target logon ID[endgameTargetLogonId])via[processName](123)as requested by subject[endgameSubjectUserName]\\[endgameSubjectDomainName](subject logon ID[endgameSubjectLogonId])[eventCode]' + ); + }); + + test('it renders the expected text when winlogEventId is NOT provided', () => { + const wrapper = mountWithIntl( + + + + ); + expect(wrapper.text()).toEqual( + 'With special privileges,[userName]\\[userDomain]@[hostName]successfully logged inusing logon type2 - Interactive(target logon ID[endgameTargetLogonId])via[processName](123)as requested by subject[endgameSubjectUserName]\\[endgameSubjectDomainName](subject logon ID[endgameSubjectLogonId])[eventCode]' + ); + }); + + test('it renders the expected text when BOTH eventCode and winlogEventId are NOT provided', () => { + const wrapper = mountWithIntl( + + + + ); + expect(wrapper.text()).toEqual( + 'With special privileges,[userName]\\[userDomain]@[hostName]successfully logged inusing logon type2 - Interactive(target logon ID[endgameTargetLogonId])via[processName](123)as requested by subject[endgameSubjectUserName]\\[endgameSubjectDomainName](subject logon ID[endgameSubjectLogonId])' + ); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/endgame/endgame_security_event_details_line.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/endgame/endgame_security_event_details_line.tsx new file mode 100644 index 0000000000000..97138580618ba --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/endgame/endgame_security_event_details_line.tsx @@ -0,0 +1,255 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EuiFlexGroup } from '@elastic/eui'; +import * as React from 'react'; + +import { DraggableBadge } from '../../../../draggables'; +import { isNillEmptyOrNotFinite, TokensFlexItem } from '../helpers'; +import { ProcessDraggableWithNonExistentProcess } from '../process_draggable'; +import { UserHostWorkingDir } from '../user_host_working_dir'; + +import { + getEventDetails, + getHostNameSeparator, + getHumanReadableLogonType, + getUserDomainField, + getUserNameField, + useTargetUserAndTargetDomain, +} from './helpers'; + +import * as i18n from './translations'; + +interface Props { + contextId: string; + endgameLogonType: number | null | undefined; + endgameSubjectDomainName: string | null | undefined; + endgameSubjectLogonId: string | null | undefined; + endgameSubjectUserName: string | null | undefined; + endgameTargetDomainName: string | null | undefined; + endgameTargetLogonId: string | null | undefined; + endgameTargetUserName: string | null | undefined; + eventAction: string | null | undefined; + eventCode: string | null | undefined; + hostName: string | null | undefined; + id: string; + processExecutable: string | null | undefined; + processName: string | null | undefined; + processPid: number | null | undefined; + userDomain: string | null | undefined; + userName: string | null | undefined; + winlogEventId: string | null | undefined; +} + +export const EndgameSecurityEventDetailsLine = React.memo( + ({ + contextId, + endgameLogonType, + endgameSubjectDomainName, + endgameSubjectLogonId, + endgameSubjectUserName, + endgameTargetDomainName, + endgameTargetLogonId, + endgameTargetUserName, + eventAction, + eventCode, + hostName, + id, + processExecutable, + processName, + processPid, + userDomain, + userName, + winlogEventId, + }) => { + const domain = useTargetUserAndTargetDomain(eventAction) ? endgameTargetDomainName : userDomain; + const eventDetails = getEventDetails(eventAction); + const hostNameSeparator = getHostNameSeparator(eventAction); + const user = useTargetUserAndTargetDomain(eventAction) ? endgameTargetUserName : userName; + const userDomainField = getUserDomainField(eventAction); + const userNameField = getUserNameField(eventAction); + + return ( + <> + + {eventAction === 'admin_logon' && ( + + {i18n.WITH_SPECIAL_PRIVILEGES} + + )} + + {eventAction === 'explicit_user_logon' && ( + + {i18n.A_LOGIN_WAS_ATTEMPTED_USING_EXPLICIT_CREDENTIALS} + + )} + + + + + {eventDetails} + + + {!isNillEmptyOrNotFinite(endgameLogonType) && ( + <> + + {i18n.USING_LOGON_TYPE} + + + + + + )} + + {!isNillEmptyOrNotFinite(endgameTargetLogonId) && ( + <> + + {'('} + + + {i18n.TARGET_LOGON_ID} + + + + + + {')'} + + + )} + + + {i18n.VIA} + + + + + + + {!isNillEmptyOrNotFinite(endgameSubjectUserName) && ( + <> + + {i18n.AS_REQUESTED_BY_SUBJECT} + + + + + + + )} + + {endgameSubjectDomainName != null && ( + <> + + {'\\'} + + + + + + )} + + {!isNillEmptyOrNotFinite(endgameSubjectLogonId) && ( + <> + + {'('} + + + {i18n.SUBJECT_LOGON_ID} + + + + + + {')'} + + + )} + + {(!isNillEmptyOrNotFinite(eventCode) || !isNillEmptyOrNotFinite(winlogEventId)) && ( + <> + {!isNillEmptyOrNotFinite(eventCode) ? ( + + + + ) : ( + + + + )} + + )} + + + ); + } +); + +EndgameSecurityEventDetailsLine.displayName = 'EndgameSecurityEventDetailsLine'; diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/endgame/helpers.test.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/endgame/helpers.test.tsx new file mode 100644 index 0000000000000..06a24730242a8 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/endgame/helpers.test.tsx @@ -0,0 +1,208 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + getHostNameSeparator, + getHumanReadableLogonType, + useTargetUserAndTargetDomain, + getUserDomainField, + getUserNameField, + getEventDetails, +} from './helpers'; + +describe('helpers', () => { + describe('#getHumanReadableLogonType', () => { + test('it returns an empty string when endgameLogonType is undefined', () => { + expect(getHumanReadableLogonType(undefined)).toEqual(''); + }); + + test('it returns an empty string when endgameLogonType is null', () => { + expect(getHumanReadableLogonType(null)).toEqual(''); + }); + + test('it returns an empty string when endgameLogonType is NaN', () => { + expect(getHumanReadableLogonType(NaN)).toEqual(''); + }); + + test('it returns an empty string when endgameLogonType is Infinity', () => { + expect(getHumanReadableLogonType(Infinity)).toEqual(''); + }); + + test('it returns a string "0" given 0, an unknown logon type', () => { + expect(getHumanReadableLogonType(0)).toEqual('0'); + }); + + test('it returns a string "-1" given -1, an unknown logon type', () => { + expect(getHumanReadableLogonType(-1)).toEqual('-1'); + }); + + test('it returns "Interactive" given 2', () => { + expect(getHumanReadableLogonType(2)).toEqual('Interactive'); + }); + + test('it returns "Network" given 3', () => { + expect(getHumanReadableLogonType(3)).toEqual('Network'); + }); + + test('it returns "Batch" given 4', () => { + expect(getHumanReadableLogonType(4)).toEqual('Batch'); + }); + + test('it returns "Service" given 5', () => { + expect(getHumanReadableLogonType(5)).toEqual('Service'); + }); + + test('it returns "Unlock" given 7', () => { + expect(getHumanReadableLogonType(7)).toEqual('Unlock'); + }); + + test('it returns "Network Cleartext" given 8', () => { + expect(getHumanReadableLogonType(8)).toEqual('Network Cleartext'); + }); + + test('it returns "New Credentials" given 9', () => { + expect(getHumanReadableLogonType(9)).toEqual('New Credentials'); + }); + + test('it returns "Remote Interactive" given 10', () => { + expect(getHumanReadableLogonType(10)).toEqual('Remote Interactive'); + }); + + test('it returns "Cached Interactive" given 11', () => { + expect(getHumanReadableLogonType(11)).toEqual('Cached Interactive'); + }); + }); + + describe('#getHostNameSeparator', () => { + test('it returns "@" when eventAction is undefined', () => { + expect(getHostNameSeparator(undefined)).toEqual('@'); + }); + + test('it returns "@" when eventAction is null', () => { + expect(getHostNameSeparator(null)).toEqual('@'); + }); + + test('it returns "@" when eventAction is an empty string', () => { + expect(getHostNameSeparator('')).toEqual('@'); + }); + + test('it returns "@" when eventAction is a random value', () => { + expect(getHostNameSeparator('a random value')).toEqual('@'); + }); + + test('it returns "@" when eventAction is "user_logoff"', () => { + expect(getHostNameSeparator('user_logoff')).toEqual('@'); + }); + + test('it returns "to" when eventAction is "explicit_user_logon"', () => { + expect(getHostNameSeparator('explicit_user_logon')).toEqual('to'); + }); + }); + + describe('#useTargetUserAndTargetDomain', () => { + test('it returns false when eventAction is undefined', () => { + expect(useTargetUserAndTargetDomain(undefined)).toEqual(false); + }); + + test('it returns false when eventAction is null', () => { + expect(useTargetUserAndTargetDomain(null)).toEqual(false); + }); + + test('it returns false when eventAction is an empty string', () => { + expect(useTargetUserAndTargetDomain('')).toEqual(false); + }); + + test('it returns false when eventAction is a random value', () => { + expect(useTargetUserAndTargetDomain('a random value')).toEqual(false); + }); + + test('it returns true when eventAction is "explicit_user_logon"', () => { + expect(useTargetUserAndTargetDomain('explicit_user_logon')).toEqual(true); + }); + + test('it returns true when eventAction is "user_logoff"', () => { + expect(useTargetUserAndTargetDomain('user_logoff')).toEqual(true); + }); + }); + + describe('#getUserDomainField', () => { + test('it returns user.domain when eventAction is undefined', () => { + expect(getUserDomainField(undefined)).toEqual('user.domain'); + }); + + test('it returns user.domain when eventAction is null', () => { + expect(getUserDomainField(null)).toEqual('user.domain'); + }); + + test('it returns user.domain when eventAction is an empty string', () => { + expect(getUserDomainField('')).toEqual('user.domain'); + }); + + test('it returns user.domain when eventAction is a random value', () => { + expect(getUserDomainField('a random value')).toEqual('user.domain'); + }); + + test('it returns endgame.target_domain_name when eventAction is "explicit_user_logon"', () => { + expect(getUserDomainField('explicit_user_logon')).toEqual('endgame.target_domain_name'); + }); + + test('it returns endgame.target_domain_name when eventAction is "user_logoff"', () => { + expect(getUserDomainField('user_logoff')).toEqual('endgame.target_domain_name'); + }); + }); + + describe('#getUserNameField', () => { + test('it returns user.name when eventAction is undefined', () => { + expect(getUserNameField(undefined)).toEqual('user.name'); + }); + + test('it returns user.name when eventAction is null', () => { + expect(getUserNameField(null)).toEqual('user.name'); + }); + + test('it returns user.name when eventAction is an empty string', () => { + expect(getUserNameField('')).toEqual('user.name'); + }); + + test('it returns user.name when eventAction is a random value', () => { + expect(getUserNameField('a random value')).toEqual('user.name'); + }); + + test('it returns endgame.target_user_name when eventAction is "explicit_user_logon"', () => { + expect(getUserNameField('explicit_user_logon')).toEqual('endgame.target_user_name'); + }); + + test('it returns endgame.target_user_name when eventAction is "user_logoff"', () => { + expect(getUserNameField('user_logoff')).toEqual('endgame.target_user_name'); + }); + }); + + describe('#getEventDetails', () => { + test('it returns successfully logged in when eventAction is undefined', () => { + expect(getEventDetails(undefined)).toEqual('successfully logged in'); + }); + + test('it returns successfully logged in when eventAction is null', () => { + expect(getEventDetails(null)).toEqual('successfully logged in'); + }); + + test('it returns successfully logged in when eventAction is an empty string', () => { + expect(getEventDetails('')).toEqual('successfully logged in'); + }); + + test('it returns successfully logged in when eventAction is a random value', () => { + expect(getEventDetails('a random value')).toEqual('successfully logged in'); + }); + + test('it returns an empty string when eventAction is "explicit_user_logon"', () => { + expect(getEventDetails('explicit_user_logon')).toEqual(''); + }); + + test('it returns logged off when eventAction is "user_logoff"', () => { + expect(getEventDetails('user_logoff')).toEqual('logged off'); + }); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/endgame/helpers.ts b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/endgame/helpers.ts new file mode 100644 index 0000000000000..7470c3618fe5c --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/endgame/helpers.ts @@ -0,0 +1,61 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { isNillEmptyOrNotFinite } from '../helpers'; + +import * as i18n from './translations'; + +export const getHumanReadableLogonType = (endgameLogonType: number | null | undefined): string => { + if (isNillEmptyOrNotFinite(endgameLogonType)) { + return ''; + } + + switch (endgameLogonType) { + case 2: + return i18n.LOGON_TYPE_INTERACTIVE; + case 3: + return i18n.LOGON_TYPE_NETWORK; + case 4: + return i18n.LOGON_TYPE_BATCH; + case 5: + return i18n.LOGON_TYPE_SERVICE; + case 7: + return i18n.LOGON_TYPE_UNLOCK; + case 8: + return i18n.LOGON_TYPE_NETWORK_CLEARTEXT; + case 9: + return i18n.LOGON_TYPE_NEW_CREDENTIALS; + case 10: + return i18n.LOGON_TYPE_REMOTE_INTERACTIVE; + case 11: + return i18n.LOGON_TYPE_CACHED_INTERACTIVE; + default: + return `${endgameLogonType}`; + } +}; + +export const getHostNameSeparator = (eventAction: string | null | undefined): string => + eventAction === 'explicit_user_logon' ? i18n.TO : '@'; + +export const useTargetUserAndTargetDomain = (eventAction: string | null | undefined): boolean => + eventAction === 'explicit_user_logon' || eventAction === 'user_logoff'; + +export const getUserDomainField = (eventAction: string | null | undefined): string => + useTargetUserAndTargetDomain(eventAction) ? 'endgame.target_domain_name' : 'user.domain'; + +export const getUserNameField = (eventAction: string | null | undefined): string => + useTargetUserAndTargetDomain(eventAction) ? 'endgame.target_user_name' : 'user.name'; + +export const getEventDetails = (eventAction: string | null | undefined): string => { + switch (eventAction) { + case 'explicit_user_logon': + return ''; // no details + case 'user_logoff': + return i18n.LOGGED_OFF; + default: + return i18n.SUCCESSFULLY_LOGGED_IN; + } +}; diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/endgame/translations.ts b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/endgame/translations.ts new file mode 100644 index 0000000000000..b8c534bc1356c --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/endgame/translations.ts @@ -0,0 +1,134 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; + +export const A_LOGIN_WAS_ATTEMPTED_USING_EXPLICIT_CREDENTIALS = i18n.translate( + 'xpack.siem.timeline.body.renderers.endgame.aLoginWasAttemptedUsingExplicitCredentialsDescription', + { + defaultMessage: 'A login was attempted using explicit credentials', + } +); + +export const AS_REQUESTED_BY_SUBJECT = i18n.translate( + 'xpack.siem.timeline.body.renderers.endgame.asRequestedBySubjectDescription', + { + defaultMessage: 'as requested by subject', + } +); + +export const LOGGED_OFF = i18n.translate( + 'xpack.siem.timeline.body.renderers.endgame.loggedOffDescription', + { + defaultMessage: 'logged off', + } +); + +export const LOGON_TYPE_BATCH = i18n.translate( + 'xpack.siem.timeline.body.renderers.endgame.logonTypeBatchDescription', + { + defaultMessage: 'Batch', + } +); + +export const LOGON_TYPE_CACHED_INTERACTIVE = i18n.translate( + 'xpack.siem.timeline.body.renderers.endgame.logonTypeCachedInteractiveDescription', + { + defaultMessage: 'Cached Interactive', + } +); + +export const LOGON_TYPE_INTERACTIVE = i18n.translate( + 'xpack.siem.timeline.body.renderers.endgame.logonTypeInteractiveDescription', + { + defaultMessage: 'Interactive', + } +); + +export const LOGON_TYPE_NETWORK = i18n.translate( + 'xpack.siem.timeline.body.renderers.endgame.logonTypeNetworkDescription', + { + defaultMessage: 'Network', + } +); + +export const LOGON_TYPE_NETWORK_CLEARTEXT = i18n.translate( + 'xpack.siem.timeline.body.renderers.endgame.logonTypeNetworkCleartextDescription', + { + defaultMessage: 'Network Cleartext', + } +); + +export const LOGON_TYPE_NEW_CREDENTIALS = i18n.translate( + 'xpack.siem.timeline.body.renderers.endgame.logonTypeNewCredentialsDescription', + { + defaultMessage: 'New Credentials', + } +); + +export const LOGON_TYPE_REMOTE_INTERACTIVE = i18n.translate( + 'xpack.siem.timeline.body.renderers.endgame.logonTypeRemoteInteractiveDescription', + { + defaultMessage: 'Remote Interactive', + } +); + +export const LOGON_TYPE_SERVICE = i18n.translate( + 'xpack.siem.timeline.body.renderers.endgame.logonTypeServiceDescription', + { + defaultMessage: 'Service', + } +); + +export const LOGON_TYPE_UNLOCK = i18n.translate( + 'xpack.siem.timeline.body.renderers.endgame.logonTypeUnlockDescription', + { + defaultMessage: 'Unlock', + } +); + +export const SUBJECT_LOGON_ID = i18n.translate( + 'xpack.siem.timeline.body.renderers.endgame.subjectLogonIdDescription', + { + defaultMessage: 'subject logon ID', + } +); + +export const SUCCESSFULLY_LOGGED_IN = i18n.translate( + 'xpack.siem.timeline.body.renderers.endgame.successfullyLoggedInDescription', + { + defaultMessage: 'successfully logged in', + } +); + +export const TARGET_LOGON_ID = i18n.translate( + 'xpack.siem.timeline.body.renderers.endgame.targetLogonIdDescription', + { + defaultMessage: 'target logon ID', + } +); + +export const TO = i18n.translate('xpack.siem.timeline.body.renderers.endgame.toDescription', { + defaultMessage: 'to', +}); + +export const USING_LOGON_TYPE = i18n.translate( + 'xpack.siem.timeline.body.renderers.endgame.usingLogonTypeDescription', + { + defaultMessage: 'using logon type', + } +); + +export const VIA = i18n.translate('xpack.siem.timeline.body.renderers.endgame.viaDescription', { + defaultMessage: 'via', +}); + +export const WITH_SPECIAL_PRIVILEGES = i18n.translate( + 'xpack.siem.timeline.body.renderers.endgame.withSpecialPrivilegesDescription', + { + defaultMessage: 'With special privileges,', + } +); diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/exit_code_draggable.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/exit_code_draggable.tsx new file mode 100644 index 0000000000000..9ea5f2cdd99fa --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/exit_code_draggable.tsx @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as React from 'react'; + +import { DraggableBadge } from '../../../draggables'; + +import { isNillEmptyOrNotFinite, TokensFlexItem } from './helpers'; + +interface Props { + contextId: string; + endgameExitCode: string | null | undefined; + eventId: string; + text: string | null | undefined; +} + +export const ExitCodeDraggable = React.memo( + ({ contextId, endgameExitCode, eventId, text }) => { + if (isNillEmptyOrNotFinite(endgameExitCode)) { + return null; + } + + return ( + <> + {!isNillEmptyOrNotFinite(text) && ( + + {text} + + )} + + + + + + ); + } +); + +ExitCodeDraggable.displayName = 'ExitCodeDraggable'; diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/file_draggable.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/file_draggable.tsx new file mode 100644 index 0000000000000..af8876927d235 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/file_draggable.tsx @@ -0,0 +1,93 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as React from 'react'; + +import { DraggableBadge } from '../../../draggables'; + +import { isNillEmptyOrNotFinite, TokensFlexItem } from './helpers'; +import * as i18n from './translations'; + +interface Props { + contextId: string; + endgameFileName: string | null | undefined; + endgameFilePath: string | null | undefined; + eventId: string; + fileName: string | null | undefined; + filePath: string | null | undefined; +} + +export const FileDraggable = React.memo( + ({ contextId, endgameFileName, endgameFilePath, eventId, fileName, filePath }) => { + if ( + isNillEmptyOrNotFinite(fileName) && + isNillEmptyOrNotFinite(endgameFileName) && + isNillEmptyOrNotFinite(filePath) && + isNillEmptyOrNotFinite(endgameFilePath) + ) { + return null; + } + + const fileNameIsKnown = + !isNillEmptyOrNotFinite(fileName) || !isNillEmptyOrNotFinite(endgameFileName); + + return ( + <> + {!isNillEmptyOrNotFinite(fileName) ? ( + + + + ) : !isNillEmptyOrNotFinite(endgameFileName) ? ( + + + + ) : null} + + {fileNameIsKnown && ( + + {i18n.IN} + + )} + + {!isNillEmptyOrNotFinite(filePath) ? ( + + + + ) : !isNillEmptyOrNotFinite(endgameFilePath) ? ( + + + + ) : null} + + ); + } +); + +FileDraggable.displayName = 'FileDraggable'; diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/get_row_renderer.test.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/get_row_renderer.test.tsx index 61b4910b935e2..f7a5462be6e8f 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/get_row_renderer.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/get_row_renderer.test.tsx @@ -131,7 +131,7 @@ describe('get_column_renderer', () => { ); expect(wrapper.text()).toContain( - 'some child Braden@zeek-londonattempted a login via6278with resultfailureSource128.199.212.120' + 'some child Braden@zeek-londonattempted a login via(6278)with resultfailureSource128.199.212.120' ); }); @@ -150,7 +150,7 @@ describe('get_column_renderer', () => { ); expect(wrapper.text()).toContain( - 'some child Sessionalice@zeek-sanfranin/executedgpgconf--list-dirs agent-socket' + 'some child Sessionalice@zeek-sanfranin/executedgpgconf(5402)gpgconf--list-dirsagent-socketgpgconf --list-dirs agent-socket' ); }); }); diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/helpers.test.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/helpers.test.tsx index faaba7b92d962..2cd75344deaf6 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/helpers.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/helpers.test.tsx @@ -8,7 +8,7 @@ import { cloneDeep } from 'lodash/fp'; import { TimelineNonEcsData } from '../../../../graphql/types'; import { mockTimelineData } from '../../../../mock'; -import { deleteItemIdx, findItem, getValues } from './helpers'; +import { deleteItemIdx, findItem, getValues, isNillEmptyOrNotFinite, showVia } from './helpers'; describe('helpers', () => { describe('#deleteItemIdx', () => { @@ -123,4 +123,70 @@ describe('helpers', () => { expect(getValues('user.name', nullValue)).toBeUndefined(); }); }); + + describe('#isNillEmptyOrNotFinite', () => { + test('undefined returns true', () => { + expect(isNillEmptyOrNotFinite(undefined)).toEqual(true); + }); + + test('null returns true', () => { + expect(isNillEmptyOrNotFinite(null)).toEqual(true); + }); + + test('empty string returns true', () => { + expect(isNillEmptyOrNotFinite('')).toEqual(true); + }); + + test('empty array returns true', () => { + expect(isNillEmptyOrNotFinite([])).toEqual(true); + }); + + test('NaN returns true', () => { + expect(isNillEmptyOrNotFinite(NaN)).toEqual(true); + }); + + test('Infinity returns true', () => { + expect(isNillEmptyOrNotFinite(Infinity)).toEqual(true); + }); + + test('a single space string returns false', () => { + expect(isNillEmptyOrNotFinite(' ')).toEqual(false); + }); + + test('a simple string returns false', () => { + expect(isNillEmptyOrNotFinite('a simple string')).toEqual(false); + }); + + test('the number 0 returns false', () => { + expect(isNillEmptyOrNotFinite(0)).toEqual(false); + }); + + test('a non-empty array return false', () => { + expect(isNillEmptyOrNotFinite(['non empty array'])).toEqual(false); + }); + }); + + describe('#showVia', () => { + test('undefined returns false', () => { + expect(showVia(undefined)).toEqual(false); + }); + + test('null returns false', () => { + expect(showVia(undefined)).toEqual(false); + }); + + test('empty string false', () => { + expect(showVia('')).toEqual(false); + }); + + test('a random string returns false', () => { + expect(showVia('a random string')).toEqual(false); + }); + + ['file_create_event', 'created', 'file_delete_event', 'deleted'].forEach(eventAction => { + test(`${eventAction} returns true`, () => { + expect(showVia(eventAction)).toEqual(true); + }); + }); + }); }); diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/helpers.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/helpers.tsx index 92c7207257a14..b8e55fd29930e 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/helpers.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/helpers.tsx @@ -5,6 +5,7 @@ */ import { EuiFlexItem } from '@elastic/eui'; +import { isNumber, isEmpty } from 'lodash/fp'; import styled from 'styled-components'; import { TimelineNonEcsData } from '../../../../graphql/types'; @@ -27,6 +28,9 @@ export const getValues = (field: string, data: TimelineNonEcsData[]): string[] | export const Details = styled.div` margin: 5px 0 5px 10px; + & .euiBadge { + margin: 2px 0 2px 0; + } `; Details.displayName = 'Details'; @@ -34,3 +38,23 @@ export const TokensFlexItem = styled(EuiFlexItem)` margin-left: 3px; `; TokensFlexItem.displayName = 'TokensFlexItem'; + +export function isNillEmptyOrNotFinite(value: string | number | T[] | null | undefined) { + return isNumber(value) ? !isFinite(value) : isEmpty(value); +} + +export const isFimEvent = ({ + eventCategory, + eventDataset, +}: { + eventCategory: string | null | undefined; + eventDataset: string | null | undefined; +}) => + (eventCategory != null && eventCategory.toLowerCase() === 'file') || + (eventDataset != null && eventDataset.toLowerCase() === 'file'); + +export const isProcessStoppedOrTerminationEvent = (eventAction: string | null | undefined) => + eventAction === 'process_stopped' || eventAction === 'termination_event'; + +export const showVia = (eventAction: string | null | undefined) => + ['file_create_event', 'created', 'file_delete_event', 'deleted'].includes(`${eventAction}`); diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/parent_process_draggable.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/parent_process_draggable.tsx new file mode 100644 index 0000000000000..1423ccad7211e --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/parent_process_draggable.tsx @@ -0,0 +1,74 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as React from 'react'; + +import { DraggableBadge } from '../../../draggables'; + +import { + isNillEmptyOrNotFinite, + isProcessStoppedOrTerminationEvent, + TokensFlexItem, +} from './helpers'; + +interface Props { + contextId: string; + endgameParentProcessName: string | null | undefined; + eventAction: string | null | undefined; + eventId: string; + processPpid: number | undefined | null; + text: string | null | undefined; +} + +export const ParentProcessDraggable = React.memo( + ({ contextId, endgameParentProcessName, eventAction, eventId, processPpid, text }) => { + if ( + (isNillEmptyOrNotFinite(endgameParentProcessName) && isNillEmptyOrNotFinite(processPpid)) || + isProcessStoppedOrTerminationEvent(eventAction) + ) { + return null; + } + + return ( + <> + {!isNillEmptyOrNotFinite(text) && ( + + {text} + + )} + + {!isNillEmptyOrNotFinite(endgameParentProcessName) && ( + + + + )} + + {!isNillEmptyOrNotFinite(processPpid) && ( + + + + )} + + ); + } +); + +ParentProcessDraggable.displayName = 'ParentProcessDraggable'; diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/process.hash.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/process.hash.tsx new file mode 100644 index 0000000000000..cb0b40bdd8fca --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/process.hash.tsx @@ -0,0 +1,79 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EuiFlexGroup } from '@elastic/eui'; +import * as React from 'react'; +import styled from 'styled-components'; + +import { DraggableBadge } from '../../../draggables'; + +import { isNillEmptyOrNotFinite, TokensFlexItem } from './helpers'; + +const HashFlexGroup = styled(EuiFlexGroup)` + margin: ${({ theme }) => theme.eui.euiSizeXS}; +`; + +interface Props { + contextId: string; + eventId: string; + processHashMd5: string | null | undefined; + processHashSha1: string | null | undefined; + processHashSha256: string | null | undefined; +} + +export const ProcessHash = React.memo( + ({ contextId, eventId, processHashMd5, processHashSha1, processHashSha256 }) => { + if ( + isNillEmptyOrNotFinite(processHashSha256) && + isNillEmptyOrNotFinite(processHashSha1) && + isNillEmptyOrNotFinite(processHashMd5) + ) { + return null; + } + + return ( + + {!isNillEmptyOrNotFinite(processHashSha256) && ( + + + + )} + + {!isNillEmptyOrNotFinite(processHashSha1) && ( + + + + )} + + {!isNillEmptyOrNotFinite(processHashMd5) && ( + + + + )} + + ); + } +); + +ProcessHash.displayName = 'ProcessHash'; diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/process_draggable.test.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/process_draggable.test.tsx index 3cbe5b50f2cb2..826af00c39011 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/process_draggable.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/process_draggable.test.tsx @@ -10,7 +10,7 @@ import * as React from 'react'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; import { TestProviders } from '../../../../mock'; -import { ProcessDraggable, isNillOrEmptyString } from './process_draggable'; +import { ProcessDraggable } from './process_draggable'; describe('ProcessDraggable', () => { describe('rendering', () => { @@ -18,6 +18,8 @@ describe('ProcessDraggable', () => { const wrapper = shallow( { { { { { { /> ); - expect(wrapper.text()).toEqual('123'); + expect(wrapper.text()).toEqual('(123)'); }); - test('it returns process name if everything else is an empty string', () => { + test('it returns just process name if process.pid and endgame.pid are NaN', () => { const wrapper = mountWithIntl( { expect(wrapper.text()).toEqual('[process-name]'); }); - test('it returns process executable if everything else is an empty string', () => { + test('it returns just process executable if process.pid and endgame.pid are NaN', () => { const wrapper = mountWithIntl( { expect(wrapper.text()).toEqual('[process-executable]'); }); - test('it returns process pid if everything else is an empty string', () => { + test('it returns process executable if everything else is an empty string or NaN', () => { const wrapper = mountWithIntl( + + ); + expect(wrapper.text()).toEqual('[process-executable]'); + }); + + test('it returns endgame.process_name if everything else is an empty string or NaN', () => { + const wrapper = mountWithIntl( + + ); - expect(wrapper.text()).toEqual('123'); + expect(wrapper.text()).toEqual('[endgame-process_name]'); }); - test('it returns process name if everything is filled', () => { + test('it returns endgame.process_name and endgame.pid if everything else is an empty string or undefined', () => { const wrapper = mountWithIntl( ); - expect(wrapper.text()).toEqual('[process-name]'); + expect(wrapper.text()).toEqual('[endgame-process_name](456)'); }); - test('it returns process executable if process name is undefined', () => { + test('it returns process pid if everything else is an empty string', () => { const wrapper = mountWithIntl( ); - expect(wrapper.text()).toEqual('[process-executable]'); + expect(wrapper.text()).toEqual('(123)'); }); - test('it returns process executable if process name is an empty string', () => { + test('it returns endgame.pid if everything else is an empty string', () => { const wrapper = mountWithIntl( ); - expect(wrapper.text()).toEqual('[process-executable]'); + expect(wrapper.text()).toEqual('(456)'); }); - }); - describe('isNillOrEmptyString', () => { - test('undefined returns true', () => { - expect(isNillOrEmptyString(undefined)).toEqual(true); + test('it returns pid and process name if everything is filled', () => { + const wrapper = mountWithIntl( + + + + ); + expect(wrapper.text()).toEqual('[process-name](123)'); }); - test('null returns true', () => { - expect(isNillOrEmptyString(null)).toEqual(true); + test('it returns process pid and executable and if process name and endgame process name are null', () => { + const wrapper = mountWithIntl( + + + + ); + expect(wrapper.text()).toEqual('[process-executable](123)'); }); - test('empty string returns true', () => { - expect(isNillOrEmptyString('')).toEqual(true); + test('it returns endgame pid and executable and if process name and endgame process name are null', () => { + const wrapper = mountWithIntl( + + + + ); + expect(wrapper.text()).toEqual('[process-executable](456)'); }); - test('single space string returns false', () => { - expect(isNillOrEmptyString(' ')).toEqual(false); + test('it returns process pid and executable and if process name is undefined', () => { + const wrapper = mountWithIntl( + + + + ); + expect(wrapper.text()).toEqual('[process-executable](123)'); }); - test('regular value returns false', () => { - expect(isNillOrEmptyString('[process-name-1]')).toEqual(false); + test('it returns process pid and executable if process name is an empty string', () => { + const wrapper = mountWithIntl( + + + + ); + expect(wrapper.text()).toEqual('[process-executable](123)'); }); }); }); diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/process_draggable.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/process_draggable.tsx index a02b5099d3608..440feca8f3e56 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/process_draggable.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/process_draggable.tsx @@ -4,88 +4,120 @@ * you may not use this file except in compliance with the Elastic License. */ -import { isNumber, isString } from 'lodash/fp'; import * as React from 'react'; import { pure } from 'recompose'; import { DraggableBadge } from '../../../draggables'; +import { isNillEmptyOrNotFinite } from './helpers'; import * as i18n from './translations'; -export const isNillOrEmptyString = (value: string | number | null | undefined) => { - if (value == null) { - return true; - } else if (isString(value)) { - return value === ''; - } else if (isNumber(value)) { - return !isFinite(value); - } -}; - interface Props { contextId: string; + endgamePid: number | null | undefined; + endgameProcessName: string | null | undefined; eventId: string; processExecutable: string | undefined | null; - processPid?: number | undefined | null; - processName?: string | undefined | null; + processPid: number | undefined | null; + processName: string | undefined | null; } export const ProcessDraggable = pure( - ({ contextId, eventId, processExecutable, processName, processPid }) => { + ({ + contextId, + endgamePid, + endgameProcessName, + eventId, + processExecutable, + processName, + processPid, + }) => { if ( - !isNillOrEmptyString(processName) || - (processName === '' && - isNillOrEmptyString(processExecutable) && - isNillOrEmptyString(processPid)) - ) { - return ( - - ); - } else if ( - !isNillOrEmptyString(processExecutable) || - (processExecutable === '' && isNillOrEmptyString(processPid)) + isNillEmptyOrNotFinite(processName) && + isNillEmptyOrNotFinite(processExecutable) && + isNillEmptyOrNotFinite(endgameProcessName) && + isNillEmptyOrNotFinite(processPid) && + isNillEmptyOrNotFinite(endgamePid) ) { - return ( - - ); - } else if (processPid != null) { - return ( - - ); - } else { return null; } + + return ( +
+ {!isNillEmptyOrNotFinite(processName) ? ( + + ) : !isNillEmptyOrNotFinite(processExecutable) ? ( + + ) : !isNillEmptyOrNotFinite(endgameProcessName) ? ( + + ) : null} + + {!isNillEmptyOrNotFinite(processPid) ? ( + + ) : !isNillEmptyOrNotFinite(endgamePid) ? ( + + ) : null} +
+ ); } ); ProcessDraggable.displayName = 'ProcessDraggable'; export const ProcessDraggableWithNonExistentProcess = pure( - ({ contextId, eventId, processExecutable, processName, processPid }) => { - if (processExecutable == null && processName == null && processPid == null) { + ({ + contextId, + endgamePid, + endgameProcessName, + eventId, + processExecutable, + processName, + processPid, + }) => { + if ( + endgamePid == null && + endgameProcessName == null && + processExecutable == null && + processName == null && + processPid == null + ) { return <>{i18n.NON_EXISTENT}; } else { return ( {
); expect(wrapper.text()).toEqual( - 'Braden@zeek-london[generic-text-123]6278with resultfailureSource128.199.212.120' + 'Braden@zeek-london[generic-text-123](6278)with resultfailureSource128.199.212.120' ); }); }); @@ -69,6 +69,7 @@ describe('SystemGenericDetails', () => { sshMethod="[sshMethod-123]" sshSignature="[sshSignature-123]" text="[generic-text-123]" + userDomain="[userDomain-123]" userName="[username-123]" workingDirectory="[working-directory-123]" /> @@ -76,7 +77,7 @@ describe('SystemGenericDetails', () => {
); expect(wrapper.text()).toEqual( - '[username-123]@[hostname-123]in[working-directory-123][generic-text-123][processName-123]with result[outcome-123][sshSignature-123][sshMethod-123][packageName-123][packageVersion-123][packageSummary-123][message-123]' + '[username-123]\\[userDomain-123]@[hostname-123]in[working-directory-123][generic-text-123][processName-123](123)with result[outcome-123][sshSignature-123][sshMethod-123][packageName-123][packageVersion-123][packageSummary-123][message-123]' ); }); @@ -99,6 +100,7 @@ describe('SystemGenericDetails', () => { sshMethod={null} sshSignature={null} text={null} + userDomain={null} userName={null} workingDirectory={null} /> @@ -127,6 +129,7 @@ describe('SystemGenericDetails', () => { sshMethod={null} sshSignature={null} text={null} + userDomain={null} userName={null} workingDirectory={null} /> @@ -155,6 +158,7 @@ describe('SystemGenericDetails', () => { sshMethod={null} sshSignature={null} text={null} + userDomain={null} userName={null} workingDirectory={null} /> @@ -183,6 +187,7 @@ describe('SystemGenericDetails', () => { sshMethod={null} sshSignature={null} text={null} + userDomain={null} userName={null} workingDirectory={null} /> @@ -211,6 +216,7 @@ describe('SystemGenericDetails', () => { sshMethod={null} sshSignature={null} text={null} + userDomain={null} userName={null} workingDirectory={null} /> @@ -241,6 +247,7 @@ describe('SystemGenericDetails', () => { sshMethod={null} sshSignature={null} text={null} + userDomain={null} userName={null} workingDirectory={null} /> @@ -271,6 +278,7 @@ describe('SystemGenericDetails', () => { sshMethod={null} sshSignature={null} text={null} + userDomain={null} userName={null} workingDirectory={null} /> @@ -295,12 +303,13 @@ describe('SystemGenericDetails', () => { packageName="[packageName-123]" packageSummary="[packageSummary-123]" packageVersion="[packageVersion-123]" - processExecutable="[packageVersion-123]" + processExecutable="[processExecutable-123]" processPid={null} processName={null} sshMethod={null} sshSignature={null} text={null} + userDomain={null} userName={null} workingDirectory={null} /> @@ -308,7 +317,7 @@ describe('SystemGenericDetails', () => {
); expect(wrapper.text()).toEqual( - '[hostname-123][packageVersion-123]with result[outcome-123][packageName-123][packageVersion-123][packageSummary-123][message-123]' + '[hostname-123][processExecutable-123]with result[outcome-123][packageName-123][packageVersion-123][packageSummary-123][message-123]' ); }); @@ -325,12 +334,13 @@ describe('SystemGenericDetails', () => { packageName="[packageName-123]" packageSummary="[packageSummary-123]" packageVersion="[packageVersion-123]" - processExecutable="[packageVersion-123]" + processExecutable="[processExecutable-123]" processPid={123} processName={null} sshMethod={null} sshSignature={null} text={null} + userDomain={null} userName={null} workingDirectory={null} /> @@ -338,7 +348,7 @@ describe('SystemGenericDetails', () => {
); expect(wrapper.text()).toEqual( - '[hostname-123][packageVersion-123]with result[outcome-123][packageName-123][packageVersion-123][packageSummary-123][message-123]' + '[hostname-123][processExecutable-123](123)with result[outcome-123][packageName-123][packageVersion-123][packageSummary-123][message-123]' ); }); @@ -355,12 +365,13 @@ describe('SystemGenericDetails', () => { packageName="[packageName-123]" packageSummary="[packageSummary-123]" packageVersion="[packageVersion-123]" - processExecutable="[packageVersion-123]" + processExecutable="[processExecutable-123]" processPid={123} processName="[processName-123]" sshMethod={null} sshSignature={null} text={null} + userDomain={null} userName={null} workingDirectory={null} /> @@ -368,7 +379,7 @@ describe('SystemGenericDetails', () => {
); expect(wrapper.text()).toEqual( - '[hostname-123][processName-123]with result[outcome-123][packageName-123][packageVersion-123][packageSummary-123][message-123]' + '[hostname-123][processName-123](123)with result[outcome-123][packageName-123][packageVersion-123][packageSummary-123][message-123]' ); }); @@ -385,12 +396,13 @@ describe('SystemGenericDetails', () => { packageName="[packageName-123]" packageSummary="[packageSummary-123]" packageVersion="[packageVersion-123]" - processExecutable="[packageVersion-123]" + processExecutable="[processExecutable-123]" processPid={123} processName="[processName-123]" sshMethod="[sshMethod-123]" sshSignature={null} text={null} + userDomain={null} userName={null} workingDirectory={null} /> @@ -398,7 +410,7 @@ describe('SystemGenericDetails', () => {
); expect(wrapper.text()).toEqual( - '[hostname-123][processName-123]with result[outcome-123][sshMethod-123][packageName-123][packageVersion-123][packageSummary-123][message-123]' + '[hostname-123][processName-123](123)with result[outcome-123][sshMethod-123][packageName-123][packageVersion-123][packageSummary-123][message-123]' ); }); @@ -415,12 +427,13 @@ describe('SystemGenericDetails', () => { packageName="[packageName-123]" packageSummary="[packageSummary-123]" packageVersion="[packageVersion-123]" - processExecutable="[packageVersion-123]" + processExecutable="[processExecutable-123]" processPid={123} processName="[processName-123]" sshMethod="[sshMethod-123]" sshSignature="[sshSignature-123]" text={null} + userDomain={null} userName={null} workingDirectory={null} /> @@ -428,7 +441,7 @@ describe('SystemGenericDetails', () => { ); expect(wrapper.text()).toEqual( - '[hostname-123][processName-123]with result[outcome-123][sshSignature-123][sshMethod-123][packageName-123][packageVersion-123][packageSummary-123][message-123]' + '[hostname-123][processName-123](123)with result[outcome-123][sshSignature-123][sshMethod-123][packageName-123][packageVersion-123][packageSummary-123][message-123]' ); }); @@ -445,12 +458,13 @@ describe('SystemGenericDetails', () => { packageName="[packageName-123]" packageSummary="[packageSummary-123]" packageVersion="[packageVersion-123]" - processExecutable="[packageVersion-123]" + processExecutable="[processExecutable-123]" processPid={123} processName="[processName-123]" sshMethod="[sshMethod-123]" sshSignature="[sshSignature-123]" text="[text-123]" + userDomain={null} userName={null} workingDirectory={null} /> @@ -458,11 +472,11 @@ describe('SystemGenericDetails', () => { ); expect(wrapper.text()).toEqual( - '[hostname-123][text-123][processName-123]with result[outcome-123][sshSignature-123][sshMethod-123][packageName-123][packageVersion-123][packageSummary-123][message-123]' + '[hostname-123][text-123][processName-123](123)with result[outcome-123][sshSignature-123][sshMethod-123][packageName-123][packageVersion-123][packageSummary-123][message-123]' ); }); - test('it can return the host, message, outcome, packageName, pacakgeSummary, packageVersion, packageExecutable, processPid, processName, sshMethod, sshSignature, text, username', () => { + test('it can return the host, message, outcome, packageName, pacakgeSummary, packageVersion, packageExecutable, processPid, processName, sshMethod, sshSignature, text, userDomain, username', () => { const wrapper = mountWithIntl(
@@ -475,12 +489,13 @@ describe('SystemGenericDetails', () => { packageName="[packageName-123]" packageSummary="[packageSummary-123]" packageVersion="[packageVersion-123]" - processExecutable="[packageVersion-123]" + processExecutable="[processExecutable-123]" processPid={123} processName="[processName-123]" sshMethod="[sshMethod-123]" sshSignature="[sshSignature-123]" text="[text-123]" + userDomain="[userDomain-123]" userName="[username-123]" workingDirectory={null} /> @@ -488,11 +503,11 @@ describe('SystemGenericDetails', () => { ); expect(wrapper.text()).toEqual( - '[username-123]@[hostname-123][text-123][processName-123]with result[outcome-123][sshSignature-123][sshMethod-123][packageName-123][packageVersion-123][packageSummary-123][message-123]' + '[username-123]\\[userDomain-123]@[hostname-123][text-123][processName-123](123)with result[outcome-123][sshSignature-123][sshMethod-123][packageName-123][packageVersion-123][packageSummary-123][message-123]' ); }); - test('it can return the host, message, outcome, packageName, pacakgeSummary, packageVersion, packageExecutable, processPid, processName, sshMethod, sshSignature, text, username, working-directory', () => { + test('it can return the host, message, outcome, packageName, pacakgeSummary, packageVersion, packageExecutable, processPid, processName, sshMethod, sshSignature, text, userDomain, username, working-directory', () => { const wrapper = mountWithIntl(
@@ -505,12 +520,13 @@ describe('SystemGenericDetails', () => { packageName="[packageName-123]" packageSummary="[packageSummary-123]" packageVersion="[packageVersion-123]" - processExecutable="[packageVersion-123]" + processExecutable="[processExecutable-123]" processPid={123} processName="[processName-123]" sshMethod="[sshMethod-123]" sshSignature="[sshSignature-123]" text="[text-123]" + userDomain="[userDomain-123]" userName="[username-123]" workingDirectory="[working-directory-123]" /> @@ -518,7 +534,7 @@ describe('SystemGenericDetails', () => { ); expect(wrapper.text()).toEqual( - '[username-123]@[hostname-123]in[working-directory-123][text-123][processName-123]with result[outcome-123][sshSignature-123][sshMethod-123][packageName-123][packageVersion-123][packageSummary-123][message-123]' + '[username-123]\\[userDomain-123]@[hostname-123]in[working-directory-123][text-123][processName-123](123)with result[outcome-123][sshSignature-123][sshMethod-123][packageName-123][packageVersion-123][packageSummary-123][message-123]' ); }); }); diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/generic_details.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/generic_details.tsx index 3284519f984fe..764a93a7294f4 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/generic_details.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/generic_details.tsx @@ -38,6 +38,7 @@ interface Props { sshMethod: string | null | undefined; sshSignature: string | null | undefined; text: string | null | undefined; + userDomain: string | null | undefined; userName: string | null | undefined; workingDirectory: string | null | undefined; } @@ -58,14 +59,16 @@ export const SystemGenericLine = pure( sshSignature, sshMethod, text, + userDomain, userName, workingDirectory, }) => ( <> - + ( ( const id = data._id; const message: string | null = data.message != null ? data.message[0] : null; const hostName: string | null | undefined = get('host.name[0]', data); + const userDomain: string | null | undefined = get('user.domain[0]', data); const userName: string | null | undefined = get('user.name[0]', data); const outcome: string | null | undefined = get('event.outcome[0]', data); const packageName: string | null | undefined = get('system.audit.package.name[0]', data); @@ -170,6 +176,7 @@ export const SystemGenericDetails = pure( sshMethod={sshMethod} sshSignature={sshSignature} text={text} + userDomain={userDomain} userName={userName} workingDirectory={workingDirectory} /> diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/generic_file_details.test.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/generic_file_details.test.tsx index 57657614c4373..91a848c8e02ed 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/generic_file_details.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/generic_file_details.test.tsx @@ -46,7 +46,7 @@ describe('SystemGenericFileDetails', () => { ); expect(wrapper.text()).toEqual( - 'Evan@zeek-london[generic-text-123]6278with resultfailureSource128.199.212.120' + 'Evan@zeek-london[generic-text-123](6278)with resultfailureSource128.199.212.120' ); }); }); @@ -59,20 +59,35 @@ describe('SystemGenericFileDetails', () => { @@ -80,7 +95,7 @@ describe('SystemGenericFileDetails', () => { ); expect(wrapper.text()).toEqual( - '[username-123]@[hostname-123]in[working-directory-123][generic-text-123][processName-123][arg-1] [arg-2] [arg-3]with result[outcome-123][sshSignature-123][sshMethod-123][packageName-123][packageVersion-123][packageSummary-123][message-123]' + '[username-123]\\[userDomain-123]@[hostname-123]in[working-directory-123][generic-text-123][fileName-123]in[filePath-123][processName-123](123)[arg-1][arg-2][arg-3][some-title-123]with exit code[endgameExitCode-123]via parent process[endgameParentProcessName-123](456)with result[outcome-123][sshSignature-123][sshMethod-123][packageName-123][packageVersion-123][packageSummary-123][processHashSha256-123][processHashSha1-123][processHashMd5-123][message-123]' ); }); @@ -90,6 +105,15 @@ describe('SystemGenericFileDetails', () => {
{ packageSummary={null} packageVersion={null} processExecutable={null} + processHashMd5={null} + processHashSha1={null} + processHashSha256={null} processPid={null} + processPpid={null} processName={null} + showMessage={true} sshMethod={null} processTitle={null} args={null} sshSignature={null} text={null} + userDomain={null} userName={null} workingDirectory={null} />
); - expect(wrapper.text()).toEqual('to an unknown process'); + expect(wrapper.text()).toEqual('an unknown process'); }); test('it can return only the host name', () => { @@ -120,6 +150,15 @@ describe('SystemGenericFileDetails', () => {
{ packageSummary={null} packageVersion={null} processExecutable={null} + processHashMd5={null} + processHashSha1={null} + processHashSha256={null} processPid={null} + processPpid={null} processName={null} + showMessage={true} sshMethod={null} sshSignature={null} text={null} + userDomain={null} userName={null} workingDirectory={null} processTitle={null} @@ -141,7 +186,7 @@ describe('SystemGenericFileDetails', () => {
); - expect(wrapper.text()).toEqual('[hostname-123]to an unknown process'); + expect(wrapper.text()).toEqual('[hostname-123]an unknown process'); }); test('it can return the host, message', () => { @@ -150,6 +195,15 @@ describe('SystemGenericFileDetails', () => {
{ packageSummary={null} packageVersion={null} processExecutable={null} + processHashMd5={null} + processHashSha1={null} + processHashSha256={null} processPid={null} + processPpid={null} processName={null} + showMessage={true} sshMethod={null} sshSignature={null} text={null} + userDomain={null} userName={null} workingDirectory={null} processTitle={null} @@ -171,7 +231,7 @@ describe('SystemGenericFileDetails', () => {
); - expect(wrapper.text()).toEqual('[hostname-123]to an unknown process[message-123]'); + expect(wrapper.text()).toEqual('[hostname-123]an unknown process[message-123]'); }); test('it can return the host, message, outcome', () => { @@ -180,6 +240,15 @@ describe('SystemGenericFileDetails', () => {
{ packageSummary={null} packageVersion={null} processExecutable={null} + processHashMd5={null} + processHashSha1={null} + processHashSha256={null} processPid={null} + processPpid={null} processName={null} + showMessage={true} sshMethod={null} sshSignature={null} text={null} + userDomain={null} userName={null} workingDirectory={null} processTitle={null} @@ -202,7 +277,7 @@ describe('SystemGenericFileDetails', () => { ); expect(wrapper.text()).toEqual( - '[hostname-123]to an unknown processwith result[outcome-123][message-123]' + '[hostname-123]an unknown processwith result[outcome-123][message-123]' ); }); @@ -212,6 +287,15 @@ describe('SystemGenericFileDetails', () => {
{ packageSummary={null} packageVersion={null} processExecutable={null} + processHashMd5={null} + processHashSha1={null} + processHashSha256={null} processPid={null} + processPpid={null} processName={null} + showMessage={true} sshMethod={null} sshSignature={null} text={null} + userDomain={null} userName={null} workingDirectory={null} processTitle={null} @@ -234,7 +324,7 @@ describe('SystemGenericFileDetails', () => { ); expect(wrapper.text()).toEqual( - '[hostname-123]to an unknown processwith result[outcome-123][packageName-123][message-123]' + '[hostname-123]an unknown processwith result[outcome-123][packageName-123][message-123]' ); }); @@ -244,6 +334,15 @@ describe('SystemGenericFileDetails', () => {
{ packageSummary="[packageSummary-123]" packageVersion={null} processExecutable={null} + processHashMd5={null} + processHashSha1={null} + processHashSha256={null} processPid={null} + processPpid={null} processName={null} + showMessage={true} sshMethod={null} sshSignature={null} text={null} + userDomain={null} userName={null} workingDirectory={null} processTitle={null} @@ -266,7 +371,7 @@ describe('SystemGenericFileDetails', () => { ); expect(wrapper.text()).toEqual( - '[hostname-123]to an unknown processwith result[outcome-123][packageName-123][packageSummary-123][message-123]' + '[hostname-123]an unknown processwith result[outcome-123][packageName-123][packageSummary-123][message-123]' ); }); @@ -276,6 +381,15 @@ describe('SystemGenericFileDetails', () => {
{ packageSummary="[packageSummary-123]" packageVersion="[packageVersion-123]" processExecutable={null} + processHashMd5={null} + processHashSha1={null} + processHashSha256={null} processPid={null} + processPpid={null} processName={null} + showMessage={true} sshMethod={null} sshSignature={null} text={null} + userDomain={null} userName={null} workingDirectory={null} processTitle={null} @@ -298,7 +418,7 @@ describe('SystemGenericFileDetails', () => { ); expect(wrapper.text()).toEqual( - '[hostname-123]to an unknown processwith result[outcome-123][packageName-123][packageVersion-123][packageSummary-123][message-123]' + '[hostname-123]an unknown processwith result[outcome-123][packageName-123][packageVersion-123][packageSummary-123][message-123]' ); }); @@ -308,6 +428,15 @@ describe('SystemGenericFileDetails', () => {
{ packageSummary="[packageSummary-123]" packageVersion="[packageVersion-123]" processExecutable="[packageVersion-123]" + processHashMd5={null} + processHashSha1={null} + processHashSha256={null} processPid={null} + processPpid={null} processName={null} + showMessage={true} sshMethod={null} sshSignature={null} text={null} + userDomain={null} userName={null} workingDirectory={null} processTitle={null} @@ -334,12 +469,21 @@ describe('SystemGenericFileDetails', () => { ); }); - test('it can return the host, message, outcome, packageName, pacakgeSummary, packageVersion, packageExecutable, processPid', () => { + test('it can return the host, message, outcome, packageName, pacakgeSummary, packageVersion, packageExecutable, processHashMd5', () => { const wrapper = mountWithIntl(
{ packageSummary="[packageSummary-123]" packageVersion="[packageVersion-123]" processExecutable="[packageVersion-123]" + processHashMd5="[processHashMd5-123]" + processHashSha1={null} + processHashSha256={null} + processPid={null} + processPpid={null} + processName={null} + showMessage={true} + sshMethod={null} + sshSignature={null} + text={null} + userDomain={null} + userName={null} + workingDirectory={null} + processTitle={null} + args={null} + /> +
+
+ ); + expect(wrapper.text()).toEqual( + '[hostname-123][packageVersion-123]with result[outcome-123][packageName-123][packageVersion-123][packageSummary-123][processHashMd5-123][message-123]' + ); + }); + + test('it can return the host, message, outcome, packageName, pacakgeSummary, packageVersion, packageExecutable, processHashMd5, processHashSha1', () => { + const wrapper = mountWithIntl( + +
+ +
+
+ ); + expect(wrapper.text()).toEqual( + '[hostname-123][packageVersion-123]with result[outcome-123][packageName-123][packageVersion-123][packageSummary-123][processHashSha1-123][processHashMd5-123][message-123]' + ); + }); + + test('it can return the host, message, outcome, packageName, pacakgeSummary, packageVersion, packageExecutable, processHashMd5, processHashSha1, processHashSha256', () => { + const wrapper = mountWithIntl( + +
+ +
+
+ ); + expect(wrapper.text()).toEqual( + '[hostname-123][packageVersion-123]with result[outcome-123][packageName-123][packageVersion-123][packageSummary-123][processHashSha256-123][processHashSha1-123][processHashMd5-123][message-123]' + ); + }); + + test('it can return the host, message, outcome, packageName, pacakgeSummary, packageVersion, packageExecutable, processHashMd5, processHashSha1, processHashSha256, processPid', () => { + const wrapper = mountWithIntl( + +
+ { ); expect(wrapper.text()).toEqual( - '[hostname-123][packageVersion-123]with result[outcome-123][packageName-123][packageVersion-123][packageSummary-123][message-123]' + '[hostname-123][processExecutable-123](123)with result[outcome-123][packageName-123][packageVersion-123][packageSummary-123][processHashSha256-123][processHashSha1-123][processHashMd5-123][message-123]' ); }); - test('it can return the host, message, outcome, packageName, pacakgeSummary, packageVersion, packageExecutable, processPid, processName', () => { + test('it can return the host, message, outcome, packageName, pacakgeSummary, packageVersion, packageExecutable, processHashMd5, processHashSha1, processHashSha256, processPid, processPpid, processName', () => { const wrapper = mountWithIntl(
{ packageSummary="[packageSummary-123]" packageVersion="[packageVersion-123]" processExecutable="[packageVersion-123]" + processHashMd5="[processHashMd5-123]" + processHashSha1="[processHashSha1-123]" + processHashSha256="[processHashSha256-123]" processPid={123} + processPpid={456} processName="[processName-123]" + showMessage={true} sshMethod={null} sshSignature={null} text={null} + userDomain={null} userName={null} workingDirectory={null} processTitle={null} @@ -394,16 +700,25 @@ describe('SystemGenericFileDetails', () => { ); expect(wrapper.text()).toEqual( - '[hostname-123][processName-123]with result[outcome-123][packageName-123][packageVersion-123][packageSummary-123][message-123]' + '[hostname-123][processName-123](123)via parent process(456)with result[outcome-123][packageName-123][packageVersion-123][packageSummary-123][processHashSha256-123][processHashSha1-123][processHashMd5-123][message-123]' ); }); - test('it can return the host, message, outcome, packageName, pacakgeSummary, packageVersion, packageExecutable, processPid, processName, sshMethod', () => { + test('it can return the endgameExitCode, endgameParentProcessName, eventAction, host, message, outcome, packageName, pacakgeSummary, packageVersion, packageExecutable, processHashMd5, processHashSha1, processHashSha256, processPid, processPpid, processName, sshMethod', () => { const wrapper = mountWithIntl(
{ packageSummary="[packageSummary-123]" packageVersion="[packageVersion-123]" processExecutable="[packageVersion-123]" + processHashMd5="[processHashMd5-123]" + processHashSha1="[processHashSha1-123]" + processHashSha256="[processHashSha256-123]" processPid={123} + processPpid={456} processName="[processName-123]" + showMessage={true} sshMethod="[sshMethod-123]" sshSignature={null} text={null} + userDomain={null} userName={null} workingDirectory={null} processTitle={null} @@ -426,16 +747,25 @@ describe('SystemGenericFileDetails', () => { ); expect(wrapper.text()).toEqual( - '[hostname-123][processName-123]with result[outcome-123][sshMethod-123][packageName-123][packageVersion-123][packageSummary-123][message-123]' + '[hostname-123][processName-123](123)with exit code[endgameExitCode-123]via parent process[endgameParentProcessName-123](456)with result[outcome-123][sshMethod-123][packageName-123][packageVersion-123][packageSummary-123][processHashSha256-123][processHashSha1-123][processHashMd5-123][message-123]' ); }); - test('it can return the host, message, outcome, packageName, pacakgeSummary, packageVersion, packageExecutable, processPid, processName, sshMethod, sshSignature', () => { + test('it can return the endgameExitCode, endgameParentProcessName, eventAction, host, message, outcome, packageName, pacakgeSummary, packageVersion, packageExecutable, processHashMd5, processHashSha1, processHashSha256, processPid, processPpid, processName, sshMethod, sshSignature', () => { const wrapper = mountWithIntl(
{ packageSummary="[packageSummary-123]" packageVersion="[packageVersion-123]" processExecutable="[packageVersion-123]" + processHashMd5="[processHashMd5-123]" + processHashSha1="[processHashSha1-123]" + processHashSha256="[processHashSha256-123]" processPid={123} + processPpid={456} processName="[processName-123]" + showMessage={true} sshMethod="[sshMethod-123]" sshSignature="[sshSignature-123]" text={null} + userDomain={null} + userName={null} + workingDirectory={null} + processTitle={null} + args={null} + /> +
+
+ ); + expect(wrapper.text()).toEqual( + '[hostname-123][processName-123](123)with exit code[endgameExitCode-123]via parent process[endgameParentProcessName-123](456)with result[outcome-123][sshSignature-123][sshMethod-123][packageName-123][packageVersion-123][packageSummary-123][processHashSha256-123][processHashSha1-123][processHashMd5-123][message-123]' + ); + }); + + test('it can return the endgameExitCode, endgameParentProcessName, eventAction, host, message, outcome, packageName, pacakgeSummary, packageVersion, packageExecutable, processHashMd5, processHashSha1, processHashSha256, processPid, processPpid, processName, sshMethod, sshSignature, text', () => { + const wrapper = mountWithIntl( + +
+ { ); expect(wrapper.text()).toEqual( - '[hostname-123][processName-123]with result[outcome-123][sshSignature-123][sshMethod-123][packageName-123][packageVersion-123][packageSummary-123][message-123]' + '[hostname-123][text-123][processName-123](123)with exit code[endgameExitCode-123]via parent process[endgameParentProcessName-123](456)with result[outcome-123][sshSignature-123][sshMethod-123][packageName-123][packageVersion-123][packageSummary-123][processHashSha256-123][processHashSha1-123][processHashMd5-123][message-123]' ); }); - test('it can return the host, message, outcome, packageName, pacakgeSummary, packageVersion, packageExecutable, processPid, processName, sshMethod, sshSignature, text', () => { + test('it can return the endgameExitCode, endgameParentProcessName, eventAction, host, message, outcome, packageName, pacakgeSummary, packageVersion, packageExecutable, processHashMd5, processHashSha1, processHashSha256, processPid, processPpid, processName, sshMethod, sshSignature, text, userDomain', () => { const wrapper = mountWithIntl(
{ packageSummary="[packageSummary-123]" packageVersion="[packageVersion-123]" processExecutable="[packageVersion-123]" + processHashMd5="[processHashMd5-123]" + processHashSha1="[processHashSha1-123]" + processHashSha256="[processHashSha256-123]" processPid={123} + processPpid={456} processName="[processName-123]" + showMessage={true} sshMethod="[sshMethod-123]" sshSignature="[sshSignature-123]" text="[text-123]" + userDomain="[userDomain-123]" userName={null} workingDirectory={null} processTitle={null} @@ -490,16 +888,25 @@ describe('SystemGenericFileDetails', () => { ); expect(wrapper.text()).toEqual( - '[hostname-123][text-123][processName-123]with result[outcome-123][sshSignature-123][sshMethod-123][packageName-123][packageVersion-123][packageSummary-123][message-123]' + '\\[userDomain-123][hostname-123][text-123][processName-123](123)with exit code[endgameExitCode-123]via parent process[endgameParentProcessName-123](456)with result[outcome-123][sshSignature-123][sshMethod-123][packageName-123][packageVersion-123][packageSummary-123][processHashSha256-123][processHashSha1-123][processHashMd5-123][message-123]' ); }); - test('it can return the host, message, outcome, packageName, pacakgeSummary, packageVersion, packageExecutable, processPid, processName, sshMethod, sshSignature, text, username', () => { + test('it can return the endgameExitCode, endgameParentProcessName, eventAction, host, message, outcome, packageName, pacakgeSummary, packageVersion, packageExecutable, processHashMd5, processHashSha1, processHashSha256, processPid, processPpid, processName, sshMethod, sshSignature, text, userDomain, username', () => { const wrapper = mountWithIntl(
{ packageSummary="[packageSummary-123]" packageVersion="[packageVersion-123]" processExecutable="[packageVersion-123]" + processHashMd5="[processHashMd5-123]" + processHashSha1="[processHashSha1-123]" + processHashSha256="[processHashSha256-123]" processPid={123} + processPpid={456} processName="[processName-123]" + showMessage={true} sshMethod="[sshMethod-123]" sshSignature="[sshSignature-123]" text="[text-123]" + userDomain="[userDomain-123]" userName="[username-123]" workingDirectory={null} processTitle={null} @@ -522,16 +935,25 @@ describe('SystemGenericFileDetails', () => { ); expect(wrapper.text()).toEqual( - '[username-123]@[hostname-123][text-123][processName-123]with result[outcome-123][sshSignature-123][sshMethod-123][packageName-123][packageVersion-123][packageSummary-123][message-123]' + '[username-123]\\[userDomain-123]@[hostname-123][text-123][processName-123](123)with exit code[endgameExitCode-123]via parent process[endgameParentProcessName-123](456)with result[outcome-123][sshSignature-123][sshMethod-123][packageName-123][packageVersion-123][packageSummary-123][processHashSha256-123][processHashSha1-123][processHashMd5-123][message-123]' ); }); - test('it can return the host, message, outcome, packageName, pacakgeSummary, packageVersion, packageExecutable, processPid, processName, sshMethod, sshSignature, text, username, working-directory', () => { + test('it can return the endgameExitCode, endgameParentProcessName, eventAction, host, message, outcome, packageName, pacakgeSummary, packageVersion, packageExecutable, processHashMd5, processHashSha1, processHashSha256, processPid, processPpid, processName, sshMethod, sshSignature, text, userDomain, username, working-directory', () => { const wrapper = mountWithIntl(
{ packageSummary="[packageSummary-123]" packageVersion="[packageVersion-123]" processExecutable="[packageVersion-123]" + processHashMd5="[processHashMd5-123]" + processHashSha1="[processHashSha1-123]" + processHashSha256="[processHashSha256-123]" processPid={123} + processPpid={456} processName="[processName-123]" + showMessage={true} sshMethod="[sshMethod-123]" sshSignature="[sshSignature-123]" text="[text-123]" + userDomain="[userDomain-123]" userName="[username-123]" workingDirectory="[working-directory-123]" processTitle={null} @@ -554,16 +982,25 @@ describe('SystemGenericFileDetails', () => { ); expect(wrapper.text()).toEqual( - '[username-123]@[hostname-123]in[working-directory-123][text-123][processName-123]with result[outcome-123][sshSignature-123][sshMethod-123][packageName-123][packageVersion-123][packageSummary-123][message-123]' + '[username-123]\\[userDomain-123]@[hostname-123]in[working-directory-123][text-123][processName-123](123)with exit code[endgameExitCode-123]via parent process[endgameParentProcessName-123](456)with result[outcome-123][sshSignature-123][sshMethod-123][packageName-123][packageVersion-123][packageSummary-123][processHashSha256-123][processHashSha1-123][processHashMd5-123][message-123]' ); }); - test('it can return the host, message, outcome, packageName, pacakgeSummary, packageVersion, packageExecutable, processPid, processName, sshMethod, sshSignature, text, username, working-directory, process-title', () => { + test('it can return the endgameExitCode, endgameParentProcessName, eventAction, host, message, outcome, packageName, pacakgeSummary, packageVersion, packageExecutable, processHashMd5, processHashSha1, processHashSha256, processPid, processPpid, processName, sshMethod, sshSignature, text, userDomain, username, working-directory, process-title', () => { const wrapper = mountWithIntl(
{ packageSummary="[packageSummary-123]" packageVersion="[packageVersion-123]" processExecutable="[packageVersion-123]" + processHashMd5="[processHashMd5-123]" + processHashSha1="[processHashSha1-123]" + processHashSha256="[processHashSha256-123]" processPid={123} + processPpid={456} processName="[processName-123]" + showMessage={true} sshMethod="[sshMethod-123]" sshSignature="[sshSignature-123]" text="[text-123]" + userDomain="[userDomain-123]" userName="[username-123]" workingDirectory="[working-directory-123]" processTitle="[process-title-123]" @@ -586,16 +1029,25 @@ describe('SystemGenericFileDetails', () => { ); expect(wrapper.text()).toEqual( - '[username-123]@[hostname-123]in[working-directory-123][text-123][processName-123]with result[outcome-123][sshSignature-123][sshMethod-123][packageName-123][packageVersion-123][packageSummary-123][message-123]' + '[username-123]\\[userDomain-123]@[hostname-123]in[working-directory-123][text-123][processName-123](123)[process-title-123]with exit code[endgameExitCode-123]via parent process[endgameParentProcessName-123](456)with result[outcome-123][sshSignature-123][sshMethod-123][packageName-123][packageVersion-123][packageSummary-123][processHashSha256-123][processHashSha1-123][processHashMd5-123][message-123]' ); }); - test('it can return the host, message, outcome, packageName, pacakgeSummary, packageVersion, packageExecutable, processPid, processName, sshMethod, sshSignature, text, username, working-directory, process-title, args', () => { + test('it can return the endgameExitCode, endgameParentProcessName, eventAction, host, message, outcome, packageName, pacakgeSummary, packageVersion, packageExecutable, processHashMd5, processHashSha1, processHashSha256, processPid, processPpid, processName, sshMethod, sshSignature, text, userDomain, username, working-directory, process-title, args', () => { const wrapper = mountWithIntl(
{ packageSummary="[packageSummary-123]" packageVersion="[packageVersion-123]" processExecutable="[packageVersion-123]" + processHashMd5="[processHashMd5-123]" + processHashSha1="[processHashSha1-123]" + processHashSha256="[processHashSha256-123]" processPid={123} + processPpid={456} processName="[processName-123]" + showMessage={true} sshMethod="[sshMethod-123]" sshSignature="[sshSignature-123]" text="[text-123]" + userDomain="[userDomain-123]" userName="[username-123]" workingDirectory="[working-directory-123]" processTitle="[process-title-123]" - args="[args-1] [args-2] [args-3]" + args={['[arg-1]', '[arg-2]', '[arg-3]']} />
); expect(wrapper.text()).toEqual( - '[username-123]@[hostname-123]in[working-directory-123][text-123][processName-123][args-1] [args-2] [args-3]with result[outcome-123][sshSignature-123][sshMethod-123][packageName-123][packageVersion-123][packageSummary-123][message-123]' + '[username-123]\\[userDomain-123]@[hostname-123]in[working-directory-123][text-123][processName-123](123)[arg-1][arg-2][arg-3][process-title-123]with exit code[endgameExitCode-123]via parent process[endgameParentProcessName-123](456)with result[outcome-123][sshSignature-123][sshMethod-123][packageName-123][packageVersion-123][packageSummary-123][processHashSha256-123][processHashSha1-123][processHashMd5-123][message-123]' ); }); }); diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/generic_file_details.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/generic_file_details.tsx index e2884205a7b60..4d87543dd64f2 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/generic_file_details.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/generic_file_details.tsx @@ -17,16 +17,29 @@ import { OverflowField } from '../../../../tables/helpers'; import * as i18n from './translations'; import { NetflowRenderer } from '../netflow'; import { UserHostWorkingDir } from '../user_host_working_dir'; -import { Details, TokensFlexItem } from '../helpers'; +import { Details, showVia, TokensFlexItem } from '../helpers'; import { ProcessDraggableWithNonExistentProcess } from '../process_draggable'; import { Args } from '../args'; import { AuthSsh } from './auth_ssh'; +import { ExitCodeDraggable } from '../exit_code_draggable'; +import { FileDraggable } from '../file_draggable'; import { Package } from './package'; import { Badge } from '../../../../page'; +import { ParentProcessDraggable } from '../parent_process_draggable'; +import { ProcessHash } from '../process.hash'; interface Props { - args: string | null | undefined; + args: string[] | null | undefined; contextId: string; + endgameExitCode: string | null | undefined; + endgameFileName: string | null | undefined; + endgameFilePath: string | null | undefined; + endgameParentProcessName: string | null | undefined; + endgamePid: number | null | undefined; + endgameProcessName: string | null | undefined; + eventAction: string | null | undefined; + fileName: string | null | undefined; + filePath: string | null | undefined; hostName: string | null | undefined; id: string; message: string | null | undefined; @@ -36,11 +49,17 @@ interface Props { packageVersion: string | null | undefined; processName: string | null | undefined; processPid: number | null | undefined; + processPpid: number | null | undefined; processExecutable: string | null | undefined; + processHashMd5: string | null | undefined; + processHashSha1: string | null | undefined; + processHashSha256: string | null | undefined; processTitle: string | null | undefined; + showMessage: boolean; sshSignature: string | null | undefined; sshMethod: string | null | undefined; text: string | null | undefined; + userDomain: string | null | undefined; userName: string | null | undefined; workingDirectory: string | null | undefined; } @@ -49,6 +68,15 @@ export const SystemGenericFileLine = pure( ({ args, contextId, + endgameExitCode, + endgameFileName, + endgameFilePath, + endgameParentProcessName, + endgamePid, + endgameProcessName, + eventAction, + fileName, + filePath, hostName, id, message, @@ -57,20 +85,27 @@ export const SystemGenericFileLine = pure( packageSummary, packageVersion, processExecutable, + processHashMd5, + processHashSha1, + processHashSha256, processName, processPid, + processPpid, processTitle, + showMessage, sshSignature, sshMethod, text, + userDomain, userName, workingDirectory, }) => ( <> - + ( {text} + + {showVia(eventAction) && ( + + {i18n.VIA} + + )} - + + + {outcome != null && ( {i18n.WITH_RESULT} @@ -116,7 +180,15 @@ export const SystemGenericFileLine = pure( packageVersion={packageVersion} /> - {message != null && ( + + + {message != null && showMessage && ( <> @@ -138,29 +210,46 @@ interface GenericDetailsProps { browserFields: BrowserFields; data: Ecs; contextId: string; + showMessage?: boolean; text: string; timelineId: string; } export const SystemGenericFileDetails = pure( - ({ data, contextId, text, timelineId }) => { + ({ data, contextId, showMessage = true, text, timelineId }) => { const id = data._id; const message: string | null = data.message != null ? data.message[0] : null; const hostName: string | null | undefined = get('host.name[0]', data); + const endgameExitCode: string | null | undefined = get('endgame.exit_code[0]', data); + const endgameFileName: string | null | undefined = get('endgame.file_name[0]', data); + const endgameFilePath: string | null | undefined = get('endgame.file_path[0]', data); + const endgameParentProcessName: string | null | undefined = get( + 'endgame.parent_process_name[0]', + data + ); + const endgamePid: number | null | undefined = get('endgame.pid[0]', data); + const endgameProcessName: string | null | undefined = get('endgame.process_name[0]', data); + const eventAction: string | null | undefined = get('event.action[0]', data); + const fileName: string | null | undefined = get('file.name[0]', data); + const filePath: string | null | undefined = get('file.path[0]', data); + const userDomain: string | null | undefined = get('user.domain[0]', data); const userName: string | null | undefined = get('user.name[0]', data); const outcome: string | null | undefined = get('event.outcome[0]', data); const packageName: string | null | undefined = get('system.audit.package.name[0]', data); const packageSummary: string | null | undefined = get('system.audit.package.summary[0]', data); const packageVersion: string | null | undefined = get('system.audit.package.version[0]', data); + const processHashMd5: string | null | undefined = get('process.hash.md5[0]', data); + const processHashSha1: string | null | undefined = get('process.hash.sha1[0]', data); + const processHashSha256: string | null | undefined = get('process.hash.sha256', data); const processPid: number | null | undefined = get('process.pid[0]', data); + const processPpid: number | null | undefined = get('process.ppid[0]', data); const processName: string | null | undefined = get('process.name[0]', data); const sshSignature: string | null | undefined = get('system.auth.ssh.signature[0]', data); const sshMethod: string | null | undefined = get('system.auth.ssh.method[0]', data); const processExecutable: string | null | undefined = get('process.executable[0]', data); const processTitle: string | null | undefined = get('process.title[0]', data); const workingDirectory: string | null | undefined = get('process.working_directory[0]', data); - const rawArgs: string[] | null | undefined = get('process.args', data); - const args: string | null = rawArgs != null ? rawArgs.slice(1).join(' ') : null; + const args: string[] | null | undefined = get('process.args', data); return (
@@ -169,6 +258,16 @@ export const SystemGenericFileDetails = pure( contextId={contextId} text={text} hostName={hostName} + endgameExitCode={endgameExitCode} + endgameFileName={endgameFileName} + endgameFilePath={endgameFilePath} + endgameParentProcessName={endgameParentProcessName} + endgamePid={endgamePid} + endgameProcessName={endgameProcessName} + eventAction={eventAction} + fileName={fileName} + filePath={filePath} + userDomain={userDomain} userName={userName} message={message} processTitle={processTitle} @@ -177,9 +276,14 @@ export const SystemGenericFileDetails = pure( packageName={packageName} packageSummary={packageSummary} packageVersion={packageVersion} + processHashMd5={processHashMd5} + processHashSha1={processHashSha1} + processHashSha256={processHashSha256} processName={processName} processPid={processPid} + processPpid={processPpid} processExecutable={processExecutable} + showMessage={showMessage} sshSignature={sshSignature} sshMethod={sshMethod} outcome={outcome} diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/generic_row_renderer.test.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/generic_row_renderer.test.tsx index fe25c7b62a27d..595e8f0327db8 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/generic_row_renderer.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/generic_row_renderer.test.tsx @@ -77,7 +77,7 @@ describe('GenericRowRenderer', () => { ); expect(wrapper.text()).toContain( - 'some children Evan@zeek-londonsome text6278with resultfailureSource128.199.212.120' + 'some children Evan@zeek-londonsome text(6278)with resultfailureSource128.199.212.120' ); }); }); @@ -140,7 +140,7 @@ describe('GenericRowRenderer', () => { ); expect(wrapper.text()).toContain( - 'some children Braden@zeek-londonsome text6278with resultfailureSource128.199.212.120' + 'some children Braden@zeek-londonsome text(6278)with resultfailureSource128.199.212.120' ); }); }); diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/generic_row_renderer.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/generic_row_renderer.tsx index 1b3e415fa4ccb..9000e23ee0d3a 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/generic_row_renderer.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/generic_row_renderer.tsx @@ -7,7 +7,11 @@ import { get } from 'lodash/fp'; import React from 'react'; +import { DnsRequestEventDetails } from '../dns/dns_request_event_details'; +import { EndgameSecurityEventDetails } from '../endgame/endgame_security_event_details'; +import { isFimEvent, isNillEmptyOrNotFinite } from '../helpers'; import { RowRenderer, RowRendererContainer } from '../row_renderer'; + import { SystemGenericDetails } from './generic_details'; import { SystemGenericFileDetails } from './generic_file_details'; import * as i18n from './translations'; @@ -45,6 +49,74 @@ export const createGenericSystemRowRenderer = ({ ), }); +export const createEndgameProcessRowRenderer = ({ + actionName, + text, +}: { + actionName: string; + text: string; +}): RowRenderer => ({ + isInstance: ecs => { + const action: string | null | undefined = get('event.action[0]', ecs); + const category: string | null | undefined = get('event.category[0]', ecs); + return ( + category != null && + category.toLowerCase() === 'process' && + action != null && + action.toLowerCase() === actionName + ); + }, + renderRow: ({ browserFields, data, children, timelineId }) => ( + <> + {children} + + + + + ), +}); + +export const createFimRowRenderer = ({ + actionName, + text, +}: { + actionName: string; + text: string; +}): RowRenderer => ({ + isInstance: ecs => { + const action: string | null | undefined = get('event.action[0]', ecs); + const category: string | null | undefined = get('event.category[0]', ecs); + const dataset: string | null | undefined = get('event.dataset[0]', ecs); + return ( + isFimEvent({ eventCategory: category, eventDataset: dataset }) && + action != null && + action.toLowerCase() === actionName + ); + }, + renderRow: ({ browserFields, data, children, timelineId }) => ( + <> + {children} + + + + + ), +}); + export const createGenericFileRowRenderer = ({ actionName, text, @@ -78,6 +150,84 @@ export const createGenericFileRowRenderer = ({ ), }); +export const createSocketRowRenderer = ({ + actionName, + text, +}: { + actionName: string; + text: string; +}): RowRenderer => ({ + isInstance: ecs => { + const action: string | null | undefined = get('event.action[0]', ecs); + return action != null && action.toLowerCase() === actionName; + }, + renderRow: ({ browserFields, data, children, timelineId }) => ( + <> + {children} + + + + + ), +}); + +export const createSecurityEventRowRenderer = ({ + actionName, +}: { + actionName: string; +}): RowRenderer => ({ + isInstance: ecs => { + const category: string | null | undefined = get('event.category[0]', ecs); + const action: string | null | undefined = get('event.action[0]', ecs); + return ( + category != null && + category.toLowerCase() === 'authentication' && + action != null && + action.toLowerCase() === actionName + ); + }, + renderRow: ({ browserFields, data, children, timelineId }) => ( + <> + {children} + + + + + ), +}); + +export const createDnsRowRenderer = (): RowRenderer => ({ + isInstance: ecs => { + const dnsQuestionType: string | null | undefined = get('dns.question.type[0]', ecs); + const dnsQuestionName: string | null | undefined = get('dns.question.name[0]', ecs); + return !isNillEmptyOrNotFinite(dnsQuestionType) && !isNillEmptyOrNotFinite(dnsQuestionName); + }, + renderRow: ({ browserFields, data, children, timelineId }) => ( + <> + {children} + + + + + ), +}); + const systemLoginRowRenderer = createGenericSystemRowRenderer({ actionName: 'user_login', text: i18n.ATTEMPTED_LOGIN, @@ -88,26 +238,94 @@ const systemProcessStartedRowRenderer = createGenericFileRowRenderer({ text: i18n.PROCESS_STARTED, }); +const endgameProcessStartedRowRenderer = createEndgameProcessRowRenderer({ + actionName: 'creation_event', + text: i18n.PROCESS_STARTED, +}); + const systemProcessStoppedRowRenderer = createGenericFileRowRenderer({ actionName: 'process_stopped', text: i18n.PROCESS_STOPPED, }); +const endgameProcessTerminationRowRenderer = createEndgameProcessRowRenderer({ + actionName: 'termination_event', + text: i18n.TERMINATED_PROCESS, +}); + +const endgameFileCreateEventRowRenderer = createFimRowRenderer({ + actionName: 'file_create_event', + text: i18n.CREATED_FILE, +}); + +const fimFileCreateEventRowRenderer = createFimRowRenderer({ + actionName: 'created', + text: i18n.CREATED_FILE, +}); + +const endgameFileDeleteEventRowRenderer = createFimRowRenderer({ + actionName: 'file_delete_event', + text: i18n.DELETED_FILE, +}); + +const fimFileDeletedEventRowRenderer = createFimRowRenderer({ + actionName: 'deleted', + text: i18n.DELETED_FILE, +}); + const systemExistingRowRenderer = createGenericFileRowRenderer({ actionName: 'existing_process', text: i18n.EXISTING_PROCESS, }); -const systemSocketOpenedRowRenderer = createGenericFileRowRenderer({ +const systemSocketOpenedRowRenderer = createSocketRowRenderer({ actionName: 'socket_opened', text: i18n.SOCKET_OPENED, }); -const systemSocketClosedRowRenderer = createGenericFileRowRenderer({ +const systemSocketClosedRowRenderer = createSocketRowRenderer({ actionName: 'socket_closed', text: i18n.SOCKET_CLOSED, }); +const endgameIpv4ConnectionAcceptEventRowRenderer = createSocketRowRenderer({ + actionName: 'ipv4_connection_accept_event', + text: i18n.ACCEPTED_A_CONNECTION_VIA, +}); + +const endgameIpv6ConnectionAcceptEventRowRenderer = createSocketRowRenderer({ + actionName: 'ipv6_connection_accept_event', + text: i18n.ACCEPTED_A_CONNECTION_VIA, +}); + +const endgameIpv4DisconnectReceivedEventRowRenderer = createSocketRowRenderer({ + actionName: 'ipv4_disconnect_received_event', + text: i18n.DISCONNECTED_VIA, +}); + +const endgameIpv6DisconnectReceivedEventRowRenderer = createSocketRowRenderer({ + actionName: 'ipv6_disconnect_received_event', + text: i18n.DISCONNECTED_VIA, +}); + +const endgameAdminLogonRowRenderer = createSecurityEventRowRenderer({ + actionName: 'admin_logon', +}); + +const endgameExplicitUserLogonRowRenderer = createSecurityEventRowRenderer({ + actionName: 'explicit_user_logon', +}); + +const endgameUserLogoffRowRenderer = createSecurityEventRowRenderer({ + actionName: 'user_logoff', +}); + +const endgameUserLogonRowRenderer = createSecurityEventRowRenderer({ + actionName: 'user_logon', +}); + +const dnsRowRenderer = createDnsRowRenderer(); + const systemExistingUserRowRenderer = createGenericSystemRowRenderer({ actionName: 'existing_user', text: i18n.EXISTING_USER, @@ -195,6 +413,21 @@ const systemUserRemovedRowRenderer = createGenericSystemRowRenderer({ }); export const systemRowRenderers: RowRenderer[] = [ + dnsRowRenderer, + endgameAdminLogonRowRenderer, + endgameExplicitUserLogonRowRenderer, + endgameFileCreateEventRowRenderer, + endgameFileDeleteEventRowRenderer, + endgameIpv4ConnectionAcceptEventRowRenderer, + endgameIpv6ConnectionAcceptEventRowRenderer, + endgameIpv4DisconnectReceivedEventRowRenderer, + endgameIpv6DisconnectReceivedEventRowRenderer, + endgameProcessStartedRowRenderer, + endgameProcessTerminationRowRenderer, + endgameUserLogoffRowRenderer, + endgameUserLogonRowRenderer, + fimFileCreateEventRowRenderer, + fimFileDeletedEventRowRenderer, systemAcceptedRowRenderer, systemBootRowRenderer, systemErrorRowRenderer, diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/translations.ts b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/translations.ts index be4ce4a16af87..25a23fcfea783 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/translations.ts +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/system/translations.ts @@ -32,10 +32,21 @@ export const WAS_AUTHORIZED_TO_USE = i18n.translate( } ); +export const ACCEPTED_A_CONNECTION_VIA = i18n.translate( + 'xpack.siem.system.acceptedAConnectionViaDescription', + { + defaultMessage: 'accepted a connection via', + } +); + export const ATTEMPTED_LOGIN = i18n.translate('xpack.siem.system.attemptedLoginDescription', { defaultMessage: 'attempted a login via', }); +export const DISCONNECTED_VIA = i18n.translate('xpack.siem.system.disconnectedViaDescription', { + defaultMessage: 'disconnected via', +}); + export const LOGGED_OUT = i18n.translate('xpack.siem.system.loggedOutDescription', { defaultMessage: 'logged out via', }); @@ -52,6 +63,18 @@ export const PROCESS_STOPPED = i18n.translate('xpack.siem.system.processStoppedD defaultMessage: 'stopped process', }); +export const TERMINATED_PROCESS = i18n.translate('xpack.siem.system.terminatedProcessDescription', { + defaultMessage: 'terminated process', +}); + +export const CREATED_FILE = i18n.translate('xpack.siem.system.createdFileDescription', { + defaultMessage: 'created a file', +}); + +export const DELETED_FILE = i18n.translate('xpack.siem.system.deletedFileDescription', { + defaultMessage: 'deleted a file', +}); + export const EXISTING_PROCESS = i18n.translate('xpack.siem.system.existingProcessDescription', { defaultMessage: 'is running process', }); @@ -123,3 +146,15 @@ export const PACKAGE_REMOVED = i18n.translate('xpack.siem.system.packageRemovedD export const USER_REMOVED = i18n.translate('xpack.siem.system.userRemovedDescription', { defaultMessage: 'was removed', }); + +export const VIA = i18n.translate('xpack.siem.system.viaDescription', { + defaultMessage: 'via', +}); + +export const VIA_PARENT_PROCESS = i18n.translate('xpack.siem.system.viaParentProcessDescription', { + defaultMessage: 'via parent process', +}); + +export const WITH_EXIT_CODE = i18n.translate('xpack.siem.system.withExitCodeDescription', { + defaultMessage: 'with exit code', +}); diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/translations.ts b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/translations.ts index 304b8a23aa23b..2c3c3efdb2993 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/translations.ts +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/translations.ts @@ -27,5 +27,5 @@ export const IN = i18n.translate('xpack.siem.auditd.inDescription', { }); export const NON_EXISTENT = i18n.translate('xpack.siem.auditd.nonExistentDescription', { - defaultMessage: 'to an unknown process', + defaultMessage: 'an unknown process', }); diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/user_host_working_dir.test.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/user_host_working_dir.test.tsx index 8b0df5c64d45a..cf03213fc34f3 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/user_host_working_dir.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/user_host_working_dir.test.tsx @@ -19,6 +19,7 @@ describe('UserHostWorkingDir', () => { { expect(toJson(wrapper)).toMatchSnapshot(); }); - test('it returns null if userName, hostName, and workingDirectory are all null', () => { + test('it returns null if userDomain, userName, hostName, and workingDirectory are all null', () => { const wrapper = mountWithIntl( { expect(wrapper.isEmptyRender()).toBeTruthy(); }); - test('it returns null if userName, hostName, and workingDirectory are all undefined', () => { + test('it returns null if userDomain, userName, hostName, and workingDirectory are all undefined', () => { const wrapper = mountWithIntl( { expect(wrapper.isEmptyRender()).toBeTruthy(); }); + test('it returns userDomain if that is the only attribute defined', () => { + const wrapper = mountWithIntl( + +
+ +
+
+ ); + expect(wrapper.text()).toEqual('\\[user-domain-123]'); + }); + test('it returns userName if that is the only attribute defined', () => { const wrapper = mountWithIntl( @@ -64,6 +85,7 @@ describe('UserHostWorkingDir', () => { { { { { { expect(wrapper.text()).toEqual('[host-name-123]in[working-directory-123]'); }); - test('it returns userName, hostName', () => { + test('it returns userName, userDomain, hostName', () => { const wrapper = mountWithIntl(
{
); - expect(wrapper.text()).toEqual('[user-name-123]@[host-name-123]'); + expect(wrapper.text()).toEqual('[user-name-123]\\[user-domain-123]@[host-name-123]'); }); }); }); diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/user_host_working_dir.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/user_host_working_dir.tsx index 076dbbf324766..f9ee78aaa5bde 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/user_host_working_dir.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/renderers/user_host_working_dir.tsx @@ -14,27 +14,62 @@ import { HostWorkingDir } from './host_working_dir'; interface Props { contextId: string; eventId: string; + userDomain: string | null | undefined; + userDomainField?: string; userName: string | null | undefined; + userNameField?: string; hostName: string | null | undefined; + hostNameSeparator?: string; workingDirectory: string | null | undefined; } export const UserHostWorkingDir = pure( - ({ contextId, eventId, userName, hostName, workingDirectory }) => - userName != null || hostName != null || workingDirectory != null ? ( + ({ + contextId, + eventId, + hostName, + hostNameSeparator = '@', + userDomain, + userDomainField = 'user.domain', + userName, + userNameField = 'user.name', + workingDirectory, + }) => + userName != null || userDomain != null || hostName != null || workingDirectory != null ? ( <> + + {userDomain != null && ( + <> + + {'\\'} + + + + + + )} + {hostName != null && userName != null && ( - {'@'} + {hostNameSeparator} )}