[Security Solution] [Timeline] Endpoint row renderers (1st batch) (#89810)

## [Security Solution] [Timeline] Endpoint row renderers (1st batch)

This PR implements the 1st batch of Endpoint (`event.module: "endpoint"`) row renderers by updating and enhancing some of the existing "Endgame" (`event.module: "endgame"`) row renderers to use the latest [ECS fields](https://www.elastic.co/guide/en/ecs/current/ecs-field-reference.html).

The following Endpoint events will be rendered via row renderers in Timeline:

| event.dataset            | event.action        |
|--------------------------|---------------------|
| endpoint.events.file     | creation            |
| endpoint.events.file     | deletion            |
| endpoint.events.process  | start               |
| endpoint.events.process  | end                 |
| endpoint.events.network  | lookup_requested    |
| endpoint.events.network  | lookup_result       |
| endpoint.events.network  | connection_accepted |
| endpoint.events.network  | disconnect_received |
| endpoint.events.security | log_on              |
| endpoint.events.security | log_off             |

## File (FIM) Creation events

Endpoint File (FIM) Creation events with the following `event.dataset` and `event.action` will be rendered in Timeline via row renderers:

```
event.dataset: endpoint.events.file and event.action: creation
```

### Sample rendered File (FIM) Creation event

![endpoint_file_creation](https://user-images.githubusercontent.com/4459398/106036793-ff522f80-6092-11eb-9e3b-c24538129bea.png)

Each field with `this formatting` is draggable (to pivot a search) in the row-rendered event:

`SYSTEM` \ `NT AUTHORITY` @ `win2019-endpoint` created a file `WimProvider.dll` in `C:\Windows\TEMP\F590BACBAE94\WimProvider.dll` via `MsMpEng.exe` `(2424)`

### Fields in a File (FIM) Creation event

`user.name` \ `user.domain` @ `host.name` created a file `file.name` in `file.path` via `process.name` `(process.pid)`

## File (FIM) Deletion events

Endpoint File (FIM) Deletion events with the following `event.dataset` and `event.action` will be rendered in Timeline via row renderers:

```
event.dataset: endpoint.events.file and event.action: deletion
```

### Sample rendered File (FIM) Deletion event

![endpoint_file_deletion](https://user-images.githubusercontent.com/4459398/106037520-088fcc00-6094-11eb-985d-ba8cead9fec9.png)

`SYSTEM` \ `NT AUTHORITY` @ `windows-endpoint-1` deleted a file `AM_Delta_Patch_1.329.2793.0.exe` in `C:\Windows\SoftwareDistribution\Download\Install\AM_Delta_Patch_1.329.2793.0.exe` via `svchost.exe` `(1728)`

### Fields in a File (FIM) Deletion event

`user.name` \ `user.domain` @ `host.name` deleted a file `file.name` in `file.path` via `process.name` `(process.pid)`

## Process Start events

Endpoint Process Start events with the following `event.dataset` and `event.action` will be rendered in Timeline via row renderers:

```
event.dataset: endpoint.events.process and event.action: start
```

### Sample rendered Process Start event

![creation-event](https://user-images.githubusercontent.com/4459398/106061579-c7f37b00-60b2-11eb-9bc4-224e671baa4a.png)

`SYSTEM` \ `NT AUTHORITY` @ `win2019-endpoint` started process `conhost.exe` (`376`) `C:\Windows\system32\conhost.exe` `0xffffffff` `-ForceV1` via parent process `sshd.exe` (`6460`)

`sha256 697334c236cce7d4c9e223146ee683a1219adced9729d4ae771fd6a1502a6b63`

`sha1 e19da2c35ba1c38adf12d1a472c1fcf1f1a811a7`

`md5 1b0e9b5fcb62de0787235ecca560b610`

### Fields in a Process Start event

The following fields will be used to render a Process Start event:

`user.name` \ `user.domain` @ `host.name` started process `process.name` (`process.pid`) `process.args` via parent process `process.parent.name` (`process.parent.pid`)

`process.hash.sha256`

`process.hash.sha1`

`process.hash.md5`

## Process End events

Endpoint Process End events with the following `event.dataset` and `event.action` will be rendered in  Timeline via row renderers:

```
event.dataset: endpoint.events.process and event.action: end
```

### Sample rendered Process End event

![endpoint_process_end](https://user-images.githubusercontent.com/4459398/106076527-f1b99b80-60cc-11eb-8ff8-2da78a1fcb8f.png)

`SYSTEM` \ `NT AUTHORITY` @ `win2019-endpoint` terminated process `svchost.exe` (`10392`) `C:\Windows\System32\svchost.exe` `-k` `netsvcs` `-p` `-s` `NetSetupSvc` with exit code `0` via parent process `services.exe` `(568)`

`7fd065bac18c5278777ae44908101cdfed72d26fa741367f0ad4d02020787ab6`

`a1385ce20ad79f55df235effd9780c31442aa234`

`8a0a29438052faed8a2532da50455756`

### Fields in a Process End event

The following fields will be used to render a Process End event:

`user.name` \ `user.domain` @ `host.name` terminated process `process.name` (`process.pid`) with exit code `process.exit_code` via parent process `process.parent.name` (`process.parent.pid`)

`process.hash.sha256`

`process.hash.sha1`

`process.hash.md5`

## Network (DNS) Lookup Requested events

Endpoint Network (DNS) Lookup Requested events with the following `event.dataset` and `event.action` will be rendered in Timeline via row renderers:

```
event.dataset: endpoint.events.network and event.action: lookup_requested
```

### Runtime matching criteria

All Network Lookup Requested events, including Endpoint and non-Endpoint DNS events matching the following criteria will be rendered:

```
dns.question.type: * and dns.question.name: *
```

### Sample rendered Network Lookup Requested event

![network_lookup_requested](https://user-images.githubusercontent.com/4459398/106191208-cdf76380-6167-11eb-9be7-aaf78e4cfdd3.png)

`SYSTEM` \ `NT AUTHORITY` @ `windows-endpoint-1` asked for `logging.googleapis.com` with question type `A` via `google_osconfig_agent.exe` `(4064)` `dns`

### Fields in a Network Lookup Requested event

The following fields will be used to render a Network Lookup Request event:

`user.name` \ `user.domain`  @ `host.name` asked for `dns.question.name` with question type `dns.question.type` via `process.name` `(process.pid)` `network.protocol`

## Network Lookup Result events

Endpoint Network (DNS) Lookup Result events with the following `event.dataset` and `event.action` will be rendered in Timeline via row renderers:

```
event.dataset: endpoint.events.network and event.action: lookup_result
```

### Runtime matching criteria

All Network Lookup Result events, including Endpoint and non-Endpoint DNS events matching the following criteria will be rendered:

```
dns.question.type: * and dns.question.name: *
```

### Sample rendered Network Lookup Result event

![network_lookup_result](https://user-images.githubusercontent.com/4459398/106192595-a43f3c00-6169-11eb-95bc-4ebe331f1231.png)

`SYSTEM` \ `NT AUTHORITY` @ `windows-endpoint-1` asked for `logging.googleapis.com` with question type `AAAA` via `GCEWindowsAgent.exe` `(684)` `dns`

### Fields in a Network Lookup Result event

The following fields will be used to render a Network Lookup Result event:

`user.name` \ `user.domain`  @ `host.name` asked for `dns.question.name` with question type `dns.question.type` via `process.name` `(process.pid)` `network.protocol`

## Network Connection Accepted events

Endpoint Network Connection Accepted events with the following `event.dataset` and `event.action` will be rendered in Timeline via row renderers:

```
event.dataset: endpoint.events.network and event.action: connection_accepted
````

### Sample rendered Network Connection Accepted event

![network_connection_accepted](https://user-images.githubusercontent.com/4459398/106200497-4f54f300-6174-11eb-8879-06b7bfc88edf.png)

Network Connection Accepted events, like the one in the screenshot above, are also rendered by the _Netflow_ row renderer, which displays information that includes the directionality of the connection, protocol, and source / destination details.

`NETWORK SERVICE` \ `NT AUTHORITY` @ `windows-endpoint-1` accepted a connection via `svchost.exe` `(328)` with result `success`

### Fields in a Network Connection Accepted event

`user.name` \ `user.domain` @ `host.name` accepted a connection via `process.name` `(process.pid)` with result `event.outcome`

## Network Disconnect Received events

Endpoint Network Disconnect Received events with the following `event.dataset` and `event.action` will be rendered in Timeline via row renderers:

```
event.dataset: endpoint.events.network and event.action: disconnect_received
````

### Sample rendered Network Disconnect Received event

![network_disconnect_received](https://user-images.githubusercontent.com/4459398/106205196-56cbca80-617b-11eb-83d3-26aa9670f114.png)

Network Disconnect Received events, like the one in the screenshot above, are also rendered by the _Netflow_ row renderer, which displays information that includes the directionality of the connection, protocol, and source / destination details.

`NETWORK SERVICE` \ `NT AUTHORITY` @ `windows-endpoint-1` disconnected via `svchost.exe` `(328)`

### Fields in a Network Disconnect Received event

`user.name` \ `user.domain` @ `host.name` disconnected via `process.name` `(process.pid)`

## Security Log On events

Endpoint Security Log On events with the following `event.dataset` and `event.action` will be rendered in Timeline via row renderers:

```
event.dataset: endpoint.events.security and event.action: log_on
```

### `event.outcome: "success"` vs `event.outcome: "failure"`

The row renderer for Security Log On events uses the `event.outcome` field to display different results for events matching:

```
event.dataset: endpoint.events.security and event.action: log_on and event.outcome: success
```

vs events matching:

```
event.dataset: endpoint.events.security and event.action: log_on and event.outcome: failure
```

### Sample rendered Security Log On / `event.outcome: "success"` event

![security_log_on_success](https://user-images.githubusercontent.com/4459398/106210917-fcd00280-6184-11eb-9c1c-564cfb375539.png)

`SYSTEM` \ `NT AUTHORITY` @ `win2019-endpoint` successfully logged in via `C:\Program Files\OpenSSH-Win64\sshd.exe`

### Fields in an Security Log On / `event.outcome: "success"` event

`user.name` \ `user.domain` @ `host.name` successfully logged in via `process.name` (`process.pid`)

### Sample rendered Security Log On / `event.outcome: "failure"` event

![security_log_on_failure](https://user-images.githubusercontent.com/4459398/106211893-b2e81c00-6186-11eb-9c34-43227c15a1f0.png)

`SYSTEM` \ `NT AUTHORITY` @ `win2019-endpoint` failed to log in via `C:\Program Files\OpenSSH-Win64\sshd.exe`

### Fields in an Security Log On / `event.outcome: "failure"` event

`user.name` \ `user.domain` @ `host.name` failed to log in via `process.name` (`process.pid`)

## Security Log Off events

Endpoint Security Log Off events with the following `event.dataset` and `event.action` will be rendered in Timeline via row renderers:

```
event.dataset: endpoint.events.security and event.action: log_off
```

### Sample rendered Security Log Off event

![security_log_off](https://user-images.githubusercontent.com/4459398/106212499-0018bd80-6188-11eb-9e91-971f360ee87a.png)

`SYSTEM` \ `NT AUTHORITY` @ `win2019-endpoint` logged off via `C:\Program Files\OpenSSH-Win64\sshd.exe`

### Fields in a Security Log Off event

`user.name` \ `user.domain` @ `host.name` logged off via `process.name` (`process.pid`)
This commit is contained in:
Andrew Goldstein 2021-02-05 12:15:44 -07:00 committed by GitHub
parent b4248465cd
commit e202ceab29
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 1793 additions and 369 deletions

View file

@ -7,7 +7,9 @@
export interface ProcessEcs {
entity_id?: string[];
exit_code?: number[];
hash?: ProcessHashData;
parent?: ProcessParentData;
pid?: number[];
name?: string[];
ppid?: number[];
@ -24,6 +26,10 @@ export interface ProcessHashData {
sha256?: string[];
}
export interface ProcessParentData {
name?: string[];
}
export interface Thread {
id?: number[];
start?: string[];

View file

@ -58,6 +58,121 @@ export const mockEndgameDnsRequest: Ecs = {
},
};
export const mockEndpointNetworkLookupRequestedEvent: Ecs = {
host: {
os: {
full: ['Windows Server 2019 Datacenter 1809 (10.0.17763.1697)'],
name: ['Windows'],
version: ['1809 (10.0.17763.1697)'],
family: ['windows'],
kernel: ['1809 (10.0.17763.1697)'],
platform: ['windows'],
},
mac: ['aa:bb:cc:dd:ee:ff'],
name: ['win2019-endpoint'],
architecture: ['x86_64'],
ip: ['10.1.2.3'],
id: ['d8ad572e-d224-4044-a57d-f5a84c0dfe5d'],
},
event: {
category: ['network'],
kind: ['event'],
created: ['2021-01-25T16:44:40.788Z'],
module: ['endpoint'],
action: ['lookup_requested'],
type: ['protocol,info'],
id: ['LzzWB9jjGmCwGMvk++++6FZj'],
dataset: ['endpoint.events.network'],
},
process: {
name: ['google_osconfig_agent.exe'],
pid: [3272],
entity_id: [
'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTMyNzItMTMyNTUwNzg4NjguNjUzODkxNTAw',
],
executable: ['C:\\Program Files\\Google\\OSConfig\\google_osconfig_agent.exe'],
},
dns: {
question: {
name: ['logging.googleapis.com'],
type: ['A'],
},
},
agent: {
type: ['endpoint'],
},
user: {
name: ['SYSTEM'],
domain: ['NT AUTHORITY'],
},
network: {
protocol: ['dns'],
},
message: [
'DNS query is completed for the name logging.googleapis.com, type 1, query options 1073766400 with status 87 Results',
],
timestamp: '2021-01-25T16:44:40.788Z',
_id: 'sUNzOncBPmkOXwyN9VbT',
};
export const mockEndpointNetworkLookupResultEvent: Ecs = {
host: {
os: {
full: ['Windows Server 2019 Datacenter 1809 (10.0.17763.1697)'],
name: ['Windows'],
version: ['1809 (10.0.17763.1697)'],
family: ['windows'],
kernel: ['1809 (10.0.17763.1697)'],
platform: ['windows'],
},
mac: ['aa:bb:cc:dd:ee:ff'],
name: ['win2019-endpoint'],
architecture: ['x86_64'],
ip: ['10.1.2.3'],
id: ['d8ad572e-d224-4044-a57d-f5a84c0dfe5d'],
},
event: {
category: ['network'],
kind: ['event'],
outcome: ['success'],
created: ['2021-01-25T16:44:40.789Z'],
module: ['endpoint'],
action: ['lookup_result'],
type: ['protocol,info'],
id: ['LzzWB9jjGmCwGMvk++++6FZq'],
dataset: ['endpoint.events.network'],
},
process: {
name: ['google_osconfig_agent.exe'],
pid: [3272],
entity_id: [
'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTMyNzItMTMyNTUwNzg4NjguNjUzODkxNTAw',
],
executable: ['C:\\Program Files\\Google\\OSConfig\\google_osconfig_agent.exe'],
},
agent: {
type: ['endpoint'],
},
dns: {
question: {
name: ['logging.googleapis.com'],
type: ['AAAA'],
},
},
user: {
name: ['SYSTEM'],
domain: ['NT AUTHORITY'],
},
network: {
protocol: ['dns'],
},
message: [
'DNS query is completed for the name logging.googleapis.com, type 28, query options 2251800887582720 with status 0 Results',
],
timestamp: '2021-01-25T16:44:40.789Z',
_id: 'skNzOncBPmkOXwyN9VbT',
};
export const mockEndgameFileCreateEvent: Ecs = {
_id: '98jPcG0BOpWiDweSouzg',
user: {
@ -91,6 +206,59 @@ export const mockEndgameFileCreateEvent: Ecs = {
},
};
export const mockEndpointFileCreationEvent: Ecs = {
file: {
path: ['C:\\Windows\\TEMP\\E38FD162-B6E6-4799-B52D-F590BACBAE94\\WimProvider.dll'],
extension: ['dll'],
name: ['WimProvider.dll'],
},
host: {
os: {
full: ['Windows Server 2019 Datacenter 1809 (10.0.17763.1697)'],
name: ['Windows'],
version: ['1809 (10.0.17763.1697)'],
family: ['windows'],
kernel: ['1809 (10.0.17763.1697)'],
platform: ['windows'],
},
mac: ['aa:bb:cc:dd:ee:ff'],
name: ['win2019-endpoint'],
architecture: ['x86_64'],
ip: ['10.9.8.7'],
id: ['d8ad572e-d224-4044-a57d-f5a84c0dfe5d'],
},
event: {
category: ['file'],
kind: ['event'],
created: ['2021-01-25T16:21:56.832Z'],
module: ['endpoint'],
action: ['creation'],
type: ['creation'],
id: ['LzzWB9jjGmCwGMvk++++6FEM'],
dataset: ['endpoint.events.file'],
},
process: {
name: ['MsMpEng.exe'],
pid: [2424],
entity_id: [
'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTI0MjQtMTMyNTUwNzg2OTAuNDQ1MzY0NzAw',
],
executable: [
'C:\\ProgramData\\Microsoft\\Windows Defender\\Platform\\4.18.2011.6-0\\MsMpEng.exe',
],
},
agent: {
type: ['endpoint'],
},
user: {
name: ['SYSTEM'],
domain: ['NT AUTHORITY'],
},
message: ['Endpoint file event'],
timestamp: '2021-01-25T16:21:56.832Z',
_id: 'eSdbOncBLJMagDUQ3YFs',
};
export const mockEndgameFileDeleteEvent: Ecs = {
_id: 'OMjPcG0BOpWiDweSeuW9',
user: {
@ -123,6 +291,58 @@ export const mockEndgameFileDeleteEvent: Ecs = {
},
};
export const mockEndpointFileDeletionEvent: Ecs = {
file: {
path: ['C:\\Windows\\SoftwareDistribution\\Download\\Install\\AM_Delta_Patch_1.329.2793.0.exe'],
extension: ['exe'],
name: ['AM_Delta_Patch_1.329.2793.0.exe'],
},
host: {
os: {
full: ['Windows Server 2019 Datacenter 1809 (10.0.17763.1697)'],
name: ['Windows'],
version: ['1809 (10.0.17763.1697)'],
family: ['windows'],
kernel: ['1809 (10.0.17763.1697)'],
platform: ['windows'],
},
mac: ['11:22:33:44:55:66'],
name: ['windows-endpoint-1'],
architecture: ['x86_64'],
ip: ['10.1.2.3'],
id: ['ce6fa3c3-fda1-4984-9bce-f6d602a5bd1a'],
},
event: {
category: ['file'],
kind: ['event'],
created: ['2021-01-25T22:50:36.783Z'],
module: ['endpoint'],
action: ['deletion'],
type: ['deletion'],
id: ['Lzty2lsJxA05IUWg++++CBsc'],
dataset: ['endpoint.events.file'],
},
process: {
name: ['svchost.exe'],
pid: [1728],
entity_id: [
'YjUwNDNiMTMtYTdjNi0xZGFlLTEyZWQtODQ1ZDlhNTRhZmQyLTE3MjgtMTMyNTQ5ODc2MjYuNjg3OTg0MDAw',
],
executable: ['C:\\Windows\\System32\\svchost.exe'],
},
user: {
id: ['S-1-5-18'],
name: ['SYSTEM'],
domain: ['NT AUTHORITY'],
},
agent: {
type: ['endpoint'],
},
message: ['Endpoint file event'],
timestamp: '2021-01-25T22:50:36.783Z',
_id: 'mnXHO3cBPmkOXwyNlyv_',
};
export const mockEndgameIpv4ConnectionAcceptEvent: Ecs = {
_id: 'LsjPcG0BOpWiDweSCNfu',
user: {
@ -213,6 +433,74 @@ export const mockEndgameIpv6ConnectionAcceptEvent: Ecs = {
},
};
export const mockEndpointNetworkConnectionAcceptedEvent: Ecs = {
host: {
os: {
full: ['Windows Server 2019 Datacenter 1809 (10.0.17763.1697)'],
name: ['Windows'],
version: ['1809 (10.0.17763.1697)'],
family: ['windows'],
kernel: ['1809 (10.0.17763.1697)'],
platform: ['windows'],
},
mac: ['aa:bb:cc:dd:ee:ff'],
name: ['windows-endpoint-1'],
architecture: ['x86_64'],
ip: ['10.1.2.3'],
id: ['ce6fa3c3-fda1-4984-9bce-f6d602a5bd1a'],
},
event: {
category: ['network'],
kind: ['event'],
outcome: ['success'],
created: ['2021-01-25T16:44:45.048Z'],
module: ['endpoint'],
action: ['connection_accepted'],
type: ['start'],
id: ['Lzty2lsJxA05IUWg++++C1CY'],
dataset: ['endpoint.events.network'],
},
process: {
name: ['svchost.exe'],
pid: [328],
entity_id: [
'YjUwNDNiMTMtYTdjNi0xZGFlLTEyZWQtODQ1ZDlhNTRhZmQyLTMyOC0xMzI1NDk4NzUwNS45OTYxMjUzMDA=',
],
executable: ['C:\\Windows\\System32\\svchost.exe'],
},
source: {
geo: {
region_name: ['North Carolina'],
region_iso_code: ['US-NC'],
city_name: ['Concord'],
country_iso_code: ['US'],
continent_name: ['North America'],
country_name: ['United States'],
},
ip: ['10.1.2.3'],
port: [64557],
},
destination: {
port: [3389],
ip: ['10.50.60.70'],
},
user: {
id: ['S-1-5-20'],
name: ['NETWORK SERVICE'],
domain: ['NT AUTHORITY'],
},
agent: {
type: ['endpoint'],
},
network: {
direction: ['incoming'],
transport: ['tcp'],
},
message: ['Endpoint network event'],
timestamp: '2021-01-25T16:44:45.048Z',
_id: 'tUN0OncBPmkOXwyNOGPV',
};
export const mockEndgameIpv4DisconnectReceivedEvent: Ecs = {
_id: 'hMjPcG0BOpWiDweSoOin',
user: {
@ -309,6 +597,75 @@ export const mockEndgameIpv6DisconnectReceivedEvent: Ecs = {
},
};
export const mockEndpointDisconnectReceivedEvent: Ecs = {
host: {
os: {
full: ['Windows Server 2019 Datacenter 1809 (10.0.17763.1697)'],
name: ['Windows'],
version: ['1809 (10.0.17763.1697)'],
family: ['windows'],
kernel: ['1809 (10.0.17763.1697)'],
platform: ['windows'],
},
mac: ['aa:bb:cc:dd:ee:ff'],
name: ['windows-endpoint-1'],
architecture: ['x86_64'],
ip: ['10.1.2.3'],
id: ['ce6fa3c3-fda1-4984-9bce-f6d602a5bd1a'],
},
event: {
category: ['network'],
kind: ['event'],
created: ['2021-01-25T16:44:47.004Z'],
module: ['endpoint'],
action: ['disconnect_received'],
type: ['end'],
id: ['Lzty2lsJxA05IUWg++++C1Ch'],
dataset: ['endpoint.events.network'],
},
process: {
name: ['svchost.exe'],
pid: [328],
entity_id: [
'YjUwNDNiMTMtYTdjNi0xZGFlLTEyZWQtODQ1ZDlhNTRhZmQyLTMyOC0xMzI1NDk4NzUwNS45OTYxMjUzMDA=',
],
executable: ['C:\\Windows\\System32\\svchost.exe'],
},
source: {
geo: {
region_name: ['North Carolina'],
region_iso_code: ['US-NC'],
city_name: ['Concord'],
country_iso_code: ['US'],
continent_name: ['North America'],
country_name: ['United States'],
},
ip: ['10.20.30.40'],
port: [64557],
bytes: [1192],
},
destination: {
bytes: [1615],
port: [3389],
ip: ['10.11.12.13'],
},
user: {
id: ['S-1-5-20'],
name: ['NETWORK SERVICE'],
domain: ['NT AUTHORITY'],
},
agent: {
type: ['endpoint'],
},
network: {
direction: ['incoming'],
transport: ['tcp'],
},
message: ['Endpoint network event'],
timestamp: '2021-01-25T16:44:47.004Z',
_id: 'uUN0OncBPmkOXwyNOGPV',
};
export const mockEndgameUserLogon: Ecs = {
_id: 'QsjPcG0BOpWiDweSeuRE',
user: {
@ -357,6 +714,92 @@ export const mockEndgameUserLogon: Ecs = {
},
};
export const mockEndpointSecurityLogOnSuccessEvent: Ecs = {
host: {
os: {
full: ['Windows Server 2019 Datacenter 1809 (10.0.17763.1697)'],
name: ['Windows'],
version: ['1809 (10.0.17763.1697)'],
family: ['windows'],
kernel: ['1809 (10.0.17763.1697)'],
platform: ['windows'],
},
mac: ['aa:bb:cc:dd:ee:ff'],
name: ['win2019-endpoint'],
architecture: ['x86_64'],
ip: ['10.1.2.3'],
id: ['d8ad572e-d224-4044-a57d-f5a84c0dfe5d'],
},
event: {
category: ['authentication', 'session'],
kind: ['event'],
outcome: ['success'],
created: ['2021-01-25T16:24:51.761Z'],
module: ['endpoint'],
action: ['log_on'],
type: ['start'],
id: ['LzzWB9jjGmCwGMvk++++6FKC'],
dataset: ['endpoint.events.security'],
},
process: {
name: ['C:\\Program Files\\OpenSSH-Win64\\sshd.exe'],
entity_id: [
'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTQzNDQtMTMyNTYwNjU0ODYuMzIwNDI3MDAw',
],
executable: ['C:\\Program Files\\OpenSSH-Win64\\sshd.exe'],
pid: [90210],
},
agent: {
type: ['endpoint'],
},
user: {
name: ['SYSTEM'],
domain: ['NT AUTHORITY'],
},
message: ['Endpoint security event'],
timestamp: '2021-01-25T16:24:51.761Z',
_id: 'eSlgOncBLJMagDUQ-yBL',
};
export const mockEndpointSecurityLogOnFailureEvent: Ecs = {
host: {
os: {
full: ['Windows Server 2019 Datacenter 1809 (10.0.17763.1637)'],
name: ['Windows'],
version: ['1809 (10.0.17763.1637)'],
kernel: ['1809 (10.0.17763.1637)'],
platform: ['windows'],
family: ['windows'],
},
mac: ['aa:bb:cc:dd:ee:ff'],
ip: ['10.1.2.3'],
name: ['win2019-endpoint'],
id: ['d8ad572e-d224-4044-a57d-f5a84c0dfe5d'],
architecture: ['x86_64'],
},
event: {
category: ['authentication', 'session'],
module: ['endpoint'],
kind: ['event'],
outcome: ['failure'],
action: ['log_on'],
created: ['2020-12-28T04:05:01.409Z'],
type: ['start'],
id: ['Ly1AjdVRChqy2iq3++++3jlX'],
dataset: ['endpoint.events.security'],
},
process: {
name: ['C:\\Program Files\\OpenSSH-Win64\\sshd.exe'],
pid: [90210],
},
agent: {
type: ['endpoint'],
},
message: ['Endpoint security event'],
timestamp: '2020-12-28T04:05:01.409Z',
_id: 's8GIp3YBN9Y7_e914Upz',
};
export const mockEndgameAdminLogon: Ecs = {
_id: 'psjPcG0BOpWiDweSoelR',
user: {
@ -488,6 +931,49 @@ export const mockEndgameUserLogoff: Ecs = {
},
};
export const mockEndpointSecurityLogOffEvent: Ecs = {
host: {
os: {
full: ['Windows Server 2019 Datacenter 1809 (10.0.17763.1697)'],
name: ['Windows'],
version: ['1809 (10.0.17763.1697)'],
family: ['windows'],
kernel: ['1809 (10.0.17763.1697)'],
platform: ['windows'],
},
mac: ['aa:bb:cc:dd:ee:ff'],
name: ['win2019-endpoint'],
architecture: ['x86_64'],
ip: ['10.1.2.3'],
id: ['d8ad572e-d224-4044-a57d-f5a84c0dfe5d'],
},
event: {
category: ['authentication,session'],
kind: ['event'],
outcome: ['success'],
created: ['2021-01-26T23:27:27.610Z'],
module: ['endpoint'],
action: ['log_off'],
type: ['end'],
id: ['LzzWB9jjGmCwGMvk++++6l0y'],
dataset: ['endpoint.events.security'],
},
process: {
entity_id: [
'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTU4MC0xMzI1NTA3ODY2Ny45MTg5Njc1MDA=',
],
executable: ['C:\\Windows\\System32\\lsass.exe'],
pid: [90210],
},
user: {
name: ['SYSTEM'],
domain: ['NT AUTHORITY'],
},
message: ['Endpoint security event'],
timestamp: '2021-01-26T23:27:27.610Z',
_id: 'ZesLQXcBPmkOXwyNdT1a',
};
export const mockEndgameCreationEvent: Ecs = {
_id: 'BcjPcG0BOpWiDweSou3g',
user: {
@ -537,6 +1023,58 @@ export const mockEndgameCreationEvent: Ecs = {
},
};
export const mockEndpointProcessStartEvent: Ecs = {
process: {
hash: {
md5: ['1b0e9b5fcb62de0787235ecca560b610'],
sha256: ['697334c236cce7d4c9e223146ee683a1219adced9729d4ae771fd6a1502a6b63'],
sha1: ['e19da2c35ba1c38adf12d1a472c1fcf1f1a811a7'],
},
name: ['conhost.exe'],
pid: [3636],
entity_id: [
'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTM2MzYtMTMyNTYwODU1OTguMTA3NTA3MDAw',
],
executable: ['C:\\Windows\\System32\\conhost.exe'],
args: ['C:\\Windows\\system32\\conhost.exe,0xffffffff,-ForceV1'],
},
host: {
os: {
full: ['Windows Server 2019 Datacenter 1809 (10.0.17763.1697)'],
name: ['Windows'],
version: ['1809 (10.0.17763.1697)'],
family: ['windows'],
kernel: ['1809 (10.0.17763.1697)'],
platform: ['windows'],
},
mac: ['aa:bb:cc:dd:ee:ff'],
name: ['win2019-endpoint-1'],
architecture: ['x86_64'],
ip: ['10.1.2.3'],
id: ['d8ad572e-d224-4044-a57d-f5a84c0dfe5d'],
},
event: {
category: ['process'],
kind: ['event'],
created: ['2021-01-25T21:59:58.107Z'],
module: ['endpoint'],
action: ['start'],
type: ['start'],
id: ['LzzWB9jjGmCwGMvk++++6Kw+'],
dataset: ['endpoint.events.process'],
},
agent: {
type: ['endpoint'],
},
user: {
name: ['SYSTEM'],
domain: ['NT AUTHORITY'],
},
message: ['Endpoint process event'],
timestamp: '2021-01-25T21:59:58.107Z',
_id: 't5KSO3cB8l64wN2iQ8V9',
};
export const mockEndgameTerminationEvent: Ecs = {
_id: '2MjPcG0BOpWiDweSoutC',
user: {
@ -578,3 +1116,59 @@ export const mockEndgameTerminationEvent: Ecs = {
exit_code: [0],
},
};
export const mockEndpointProcessEndEvent: Ecs = {
process: {
hash: {
md5: ['8a0a29438052faed8a2532da50455756'],
sha256: ['7fd065bac18c5278777ae44908101cdfed72d26fa741367f0ad4d02020787ab6'],
sha1: ['a1385ce20ad79f55df235effd9780c31442aa234'],
},
name: ['svchost.exe'],
parent: {
name: ['services.exe'],
},
pid: [10392],
entity_id: [
'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTEwMzkyLTEzMjU2MjY2OTkwLjcwMzgzMDgwMA==',
],
executable: ['C:\\Windows\\System32\\svchost.exe'],
exit_code: [-1],
args: ['C:\\Windows\\System32\\svchost.exe,-k,netsvcs,-p,-s,NetSetupSvc'],
},
host: {
os: {
full: ['Windows Server 2019 Datacenter 1809 (10.0.17763.1697)'],
name: ['Windows'],
version: ['1809 (10.0.17763.1697)'],
family: ['windows'],
kernel: ['1809 (10.0.17763.1697)'],
platform: ['windows'],
},
mac: ['aa:bb:cc:dd:ee:ff'],
name: ['win2019-endpoint'],
architecture: ['x86_64'],
ip: ['10.1.2.3'],
id: ['d8ad572e-d224-4044-a57d-f5a84c0dfe5d'],
},
event: {
category: ['process'],
kind: ['event'],
created: ['2021-01-28T00:24:05.929Z'],
module: ['endpoint'],
action: ['end'],
type: ['end'],
id: ['LzzWB9jjGmCwGMvk++++77mE'],
dataset: ['endpoint.events.process'],
},
agent: {
type: ['endpoint'],
},
user: {
name: ['SYSTEM'],
domain: ['NT AUTHORITY'],
},
message: ['Endpoint process event'],
timestamp: '2021-01-28T00:24:05.929Z',
_id: 'quloRncBX5UUcOOYo2ZS',
};

View file

@ -1,20 +1,31 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`ProcessDraggable rendering it renders against shallow snapshot 1`] = `
<div>
<DraggableBadge
contextId="context-123"
eventId="event-123"
field="process.name"
iconType="console"
value="process-name-1"
/>
<DraggableBadge
contextId="context-123"
eventId="event-123"
field="process.pid"
queryValue="123"
value="(123)"
/>
</div>
<EuiFlexGroup
alignItems="center"
gutterSize="none"
>
<EuiFlexItem
grow={false}
>
<DraggableBadge
contextId="context-123"
eventId="event-123"
field="process.name"
iconType="console"
value="process-name-1"
/>
</EuiFlexItem>
<EuiFlexItem
grow={false}
>
<DraggableBadge
contextId="context-123"
eventId="event-123"
field="process.pid"
queryValue="123"
value="(123)"
/>
</EuiFlexItem>
</EuiFlexGroup>
`;

View file

@ -42,6 +42,7 @@ export const EndgameSecurityEventDetails = React.memo<Props>(({ data, contextId,
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 eventOutcome: string | null | undefined = get('event.outcome[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);
@ -64,6 +65,7 @@ export const EndgameSecurityEventDetails = React.memo<Props>(({ data, contextId,
endgameTargetUserName={endgameTargetUserName}
eventAction={eventAction}
eventCode={eventCode}
eventOutcome={eventOutcome}
hostName={hostName}
id={id}
processExecutable={processExecutable}

View file

@ -39,6 +39,7 @@ describe('EndgameSecurityEventDetailsLine', () => {
endgameTargetUserName="[endgameTargetUserName]"
eventAction="admin_logon"
eventCode="[eventCode]"
eventOutcome={undefined}
hostName="[hostName]"
id="1"
processExecutable="[processExecutable]"
@ -69,6 +70,7 @@ describe('EndgameSecurityEventDetailsLine', () => {
endgameTargetUserName="[endgameTargetUserName]"
eventAction="explicit_user_logon"
eventCode="[eventCode]"
eventOutcome={undefined}
hostName="[hostName]"
id="1"
processExecutable="[processExecutable]"
@ -99,6 +101,7 @@ describe('EndgameSecurityEventDetailsLine', () => {
endgameTargetUserName="[endgameTargetUserName]"
eventAction="explicit_user_logon"
eventCode="[eventCode]"
eventOutcome={undefined}
hostName="[hostName]"
id="1"
processExecutable="[processExecutable]"
@ -129,6 +132,7 @@ describe('EndgameSecurityEventDetailsLine', () => {
endgameTargetUserName="[endgameTargetUserName]"
eventAction="explicit_user_logon"
eventCode="[eventCode]"
eventOutcome={undefined}
hostName="[hostName]"
id="1"
processExecutable="[processExecutable]"
@ -159,6 +163,7 @@ describe('EndgameSecurityEventDetailsLine', () => {
endgameTargetUserName="[endgameTargetUserName]"
eventAction="explicit_user_logon"
eventCode="[eventCode]"
eventOutcome={undefined}
hostName="[hostName]"
id="1"
processExecutable="[processExecutable]"
@ -189,6 +194,7 @@ describe('EndgameSecurityEventDetailsLine', () => {
endgameTargetUserName="[endgameTargetUserName]"
eventAction="explicit_user_logon"
eventCode="[eventCode]"
eventOutcome={undefined}
hostName="[hostName]"
id="1"
processExecutable="[processExecutable]"
@ -219,6 +225,7 @@ describe('EndgameSecurityEventDetailsLine', () => {
endgameTargetUserName="[endgameTargetUserName]"
eventAction="explicit_user_logon"
eventCode="[eventCode]"
eventOutcome={undefined}
hostName="[hostName]"
id="1"
processExecutable="[processExecutable]"
@ -249,6 +256,7 @@ describe('EndgameSecurityEventDetailsLine', () => {
endgameTargetUserName="[endgameTargetUserName]"
eventAction="explicit_user_logon"
eventCode="[eventCode]"
eventOutcome={undefined}
hostName="[hostName]"
id="1"
processExecutable="[processExecutable]"
@ -279,6 +287,7 @@ describe('EndgameSecurityEventDetailsLine', () => {
endgameTargetUserName={undefined}
eventAction="explicit_user_logon"
eventCode="[eventCode]"
eventOutcome={undefined}
hostName="[hostName]"
id="1"
processExecutable="[processExecutable]"
@ -309,6 +318,7 @@ describe('EndgameSecurityEventDetailsLine', () => {
endgameTargetUserName="[endgameTargetUserName]"
eventAction={undefined}
eventCode="[eventCode]"
eventOutcome={undefined}
hostName="[hostName]"
id="1"
processExecutable="[processExecutable]"
@ -339,6 +349,7 @@ describe('EndgameSecurityEventDetailsLine', () => {
endgameTargetUserName="[endgameTargetUserName]"
eventAction="explicit_user_logon"
eventCode={undefined}
eventOutcome={undefined}
hostName="[hostName]"
id="1"
processExecutable="[processExecutable]"
@ -369,6 +380,7 @@ describe('EndgameSecurityEventDetailsLine', () => {
endgameTargetUserName="[endgameTargetUserName]"
eventAction="explicit_user_logon"
eventCode="[eventCode]"
eventOutcome={undefined}
hostName={undefined}
id="1"
processExecutable="[processExecutable]"
@ -399,6 +411,7 @@ describe('EndgameSecurityEventDetailsLine', () => {
endgameTargetUserName="[endgameTargetUserName]"
eventAction="explicit_user_logon"
eventCode="[eventCode]"
eventOutcome={undefined}
hostName="[hostName]"
id="1"
processExecutable={undefined}
@ -429,6 +442,7 @@ describe('EndgameSecurityEventDetailsLine', () => {
endgameTargetUserName="[endgameTargetUserName]"
eventAction="explicit_user_logon"
eventCode="[eventCode]"
eventOutcome={undefined}
hostName="[hostName]"
id="1"
processExecutable="[processExecutable]"
@ -459,6 +473,7 @@ describe('EndgameSecurityEventDetailsLine', () => {
endgameTargetUserName="[endgameTargetUserName]"
eventAction="explicit_user_logon"
eventCode="[eventCode]"
eventOutcome={undefined}
hostName="[hostName]"
id="1"
processExecutable="[processExecutable]"
@ -489,6 +504,7 @@ describe('EndgameSecurityEventDetailsLine', () => {
endgameTargetUserName="[endgameTargetUserName]"
eventAction="admin_logon"
eventCode="[eventCode]"
eventOutcome={undefined}
hostName="[hostName]"
id="1"
processExecutable="[processExecutable]"
@ -519,6 +535,7 @@ describe('EndgameSecurityEventDetailsLine', () => {
endgameTargetUserName="[endgameTargetUserName]"
eventAction="admin_logon"
eventCode="[eventCode]"
eventOutcome={undefined}
hostName="[hostName]"
id="1"
processExecutable="[processExecutable]"
@ -549,6 +566,7 @@ describe('EndgameSecurityEventDetailsLine', () => {
endgameTargetUserName="[endgameTargetUserName]"
eventAction="admin_logon"
eventCode="[eventCode]"
eventOutcome={undefined}
hostName="[hostName]"
id="1"
processExecutable="[processExecutable]"
@ -579,6 +597,7 @@ describe('EndgameSecurityEventDetailsLine', () => {
endgameTargetUserName="[endgameTargetUserName]"
eventAction="admin_logon"
eventCode={undefined}
eventOutcome={undefined}
hostName="[hostName]"
id="1"
processExecutable="[processExecutable]"

View file

@ -35,6 +35,7 @@ interface Props {
endgameTargetUserName: string | null | undefined;
eventAction: string | null | undefined;
eventCode: string | null | undefined;
eventOutcome: string | null | undefined;
hostName: string | null | undefined;
id: string;
processExecutable: string | null | undefined;
@ -57,6 +58,7 @@ export const EndgameSecurityEventDetailsLine = React.memo<Props>(
endgameTargetUserName,
eventAction,
eventCode,
eventOutcome,
hostName,
id,
processExecutable,
@ -67,7 +69,7 @@ export const EndgameSecurityEventDetailsLine = React.memo<Props>(
winlogEventId,
}) => {
const domain = getTargetUserAndTargetDomain(eventAction) ? endgameTargetDomainName : userDomain;
const eventDetails = getEventDetails(eventAction);
const eventDetails = getEventDetails({ eventAction, eventOutcome });
const hostNameSeparator = getHostNameSeparator(eventAction);
const user = getTargetUserAndTargetDomain(eventAction) ? endgameTargetUserName : userName;
const userDomainField = getUserDomainField(eventAction);

View file

@ -182,28 +182,116 @@ describe('helpers', () => {
});
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('');
expect(
getEventDetails({ eventAction: 'explicit_user_logon', eventOutcome: undefined })
).toEqual('');
});
test('it returns logged off when eventAction is "user_logoff"', () => {
expect(getEventDetails('user_logoff')).toEqual('logged off');
test('it returns logged off when eventAction is "log_off" and eventOutcome is null', () => {
expect(getEventDetails({ eventAction: 'log_off', eventOutcome: null })).toEqual('logged off');
});
test('it returns logged off when eventAction is "log_off" and eventOutcome is undefined', () => {
expect(getEventDetails({ eventAction: 'log_off', eventOutcome: undefined })).toEqual(
'logged off'
);
});
test('it returns failed to log off when eventAction is "log_off" and eventOutcome is failure', () => {
expect(getEventDetails({ eventAction: 'log_off', eventOutcome: 'failure' })).toEqual(
'failed to log off'
);
});
test('it returns failed to log off when eventAction is "log_off" and eventOutcome is fAiLuRe', () => {
expect(getEventDetails({ eventAction: 'log_off', eventOutcome: 'fAiLuRe' })).toEqual(
'failed to log off'
);
});
test('it returns logged off when eventAction is "log_off" and eventOutcome is anything_else', () => {
expect(getEventDetails({ eventAction: 'log_off', eventOutcome: 'anything_else' })).toEqual(
'logged off'
);
});
test('it returns logged off when eventAction is "user_logoff" and eventOutcome is null', () => {
expect(getEventDetails({ eventAction: 'user_logoff', eventOutcome: null })).toEqual(
'logged off'
);
});
test('it returns logged off when eventAction is "user_logoff" and eventOutcome is undefined', () => {
expect(getEventDetails({ eventAction: 'user_logoff', eventOutcome: undefined })).toEqual(
'logged off'
);
});
test('it returns failed to log off when eventAction is "user_logoff" and eventOutcome is failure', () => {
expect(getEventDetails({ eventAction: 'user_logoff', eventOutcome: 'failure' })).toEqual(
'failed to log off'
);
});
test('it returns failed to log off when eventAction is "user_logoff" and eventOutcome is fAiLuRe', () => {
expect(getEventDetails({ eventAction: 'user_logoff', eventOutcome: 'fAiLuRe' })).toEqual(
'failed to log off'
);
});
test('it returns logged off when eventAction is "user_logoff" and eventOutcome is anything_else', () => {
expect(
getEventDetails({ eventAction: 'user_logoff', eventOutcome: 'anything_else' })
).toEqual('logged off');
});
test('it returns successfully logged in when eventAction is null and eventOutcome is undefined', () => {
expect(getEventDetails({ eventAction: null, eventOutcome: undefined })).toEqual(
'successfully logged in'
);
});
test('it returns successfully logged in when eventAction is null and eventOutcome is null', () => {
expect(getEventDetails({ eventAction: null, eventOutcome: null })).toEqual(
'successfully logged in'
);
});
test('it returns successfully logged in when eventAction is undefined and eventOutcome is null', () => {
expect(getEventDetails({ eventAction: undefined, eventOutcome: null })).toEqual(
'successfully logged in'
);
});
test('it returns successfully logged in when eventAction is undefined and eventOutcome is undefined', () => {
expect(getEventDetails({ eventAction: undefined, eventOutcome: undefined })).toEqual(
'successfully logged in'
);
});
test('it returns successfully logged in when eventAction is anything_else and eventOutcome is undefined', () => {
expect(getEventDetails({ eventAction: 'anything_else', eventOutcome: undefined })).toEqual(
'successfully logged in'
);
});
test('it returns successfully logged in when eventAction is anything_else and eventOutcome is null', () => {
expect(getEventDetails({ eventAction: 'anything_else', eventOutcome: null })).toEqual(
'successfully logged in'
);
});
test('it returns failed to log in when eventAction is anything_else and eventOutcome is failure', () => {
expect(getEventDetails({ eventAction: 'anything_else', eventOutcome: 'failure' })).toEqual(
'failed to log in'
);
});
test('it returns failed to log in when eventAction is anything_else and eventOutcome is fAiLuRe', () => {
expect(getEventDetails({ eventAction: 'anything_else', eventOutcome: 'fAiLuRe' })).toEqual(
'failed to log in'
);
});
});
});

View file

@ -50,13 +50,22 @@ export const getUserDomainField = (eventAction: string | null | undefined): stri
export const getUserNameField = (eventAction: string | null | undefined): string =>
getTargetUserAndTargetDomain(eventAction) ? 'endgame.target_user_name' : 'user.name';
export const getEventDetails = (eventAction: string | null | undefined): string => {
export const getEventDetails = ({
eventAction,
eventOutcome,
}: {
eventAction: string | null | undefined;
eventOutcome: string | null | undefined;
}): string => {
switch (eventAction) {
case 'explicit_user_logon':
return ''; // no details
case 'log_off': // fall through
case 'user_logoff':
return i18n.LOGGED_OFF;
return eventOutcome?.toLowerCase() === 'failure' ? i18n.FAILED_TO_LOG_OFF : i18n.LOGGED_OFF;
default:
return i18n.SUCCESSFULLY_LOGGED_IN;
return eventOutcome?.toLowerCase() === 'failure'
? i18n.FAILED_TO_LOG_IN
: i18n.SUCCESSFULLY_LOGGED_IN;
}
};

View file

@ -21,6 +21,20 @@ export const AS_REQUESTED_BY_SUBJECT = i18n.translate(
}
);
export const FAILED_TO_LOG_IN = i18n.translate(
'xpack.securitySolution.timeline.body.renderers.endpoint.failedToLogInDescription',
{
defaultMessage: 'failed to log in',
}
);
export const FAILED_TO_LOG_OFF = i18n.translate(
'xpack.securitySolution.timeline.body.renderers.endpoint.failedToLogOffDescription',
{
defaultMessage: 'failed to log off',
}
);
export const LOGGED_OFF = i18n.translate(
'xpack.securitySolution.timeline.body.renderers.endgame.loggedOffDescription',
{

View file

@ -25,22 +25,29 @@ jest.mock('@elastic/eui', () => {
describe('ExitCodeDraggable', () => {
const mount = useMountAppended();
test('it renders the expected text and exit code, when both text and an endgameExitCode are provided', () => {
test('it renders the expected text and exit codes, when text, processExitCode, and an endgameExitCode are provided', () => {
const wrapper = mount(
<TestProviders>
<ExitCodeDraggable contextId="test" endgameExitCode="0" eventId="1" text="with exit code" />
<ExitCodeDraggable
contextId="test"
endgameExitCode="0"
eventId="1"
processExitCode={-1}
text="with exit code"
/>
</TestProviders>
);
expect(wrapper.text()).toEqual('with exit code0');
expect(wrapper.text()).toEqual('with exit code-10');
});
test('it returns an empty string when text is provided, but endgameExitCode is undefined', () => {
test('it returns an empty string when text is provided, but processExitCode and endgameExitCode are undefined', () => {
const wrapper = mount(
<TestProviders>
<ExitCodeDraggable
contextId="test"
endgameExitCode={undefined}
eventId="1"
processExitCode={undefined}
text="with exit code"
/>
</TestProviders>
@ -48,13 +55,14 @@ describe('ExitCodeDraggable', () => {
expect(wrapper.text()).toEqual('');
});
test('it returns an empty string when text is provided, but endgameExitCode is null', () => {
test('it returns an empty string when text is provided, but processExitCode and endgameExitCode are null', () => {
const wrapper = mount(
<TestProviders>
<ExitCodeDraggable
contextId="test"
endgameExitCode={null}
eventId="1"
processExitCode={null}
text="with exit code"
/>
</TestProviders>
@ -65,36 +73,105 @@ describe('ExitCodeDraggable', () => {
test('it returns an empty string when text is provided, but endgameExitCode is an empty string', () => {
const wrapper = mount(
<TestProviders>
<ExitCodeDraggable contextId="test" endgameExitCode="" eventId="1" text="with exit code" />
<ExitCodeDraggable
contextId="test"
endgameExitCode=""
eventId="1"
processExitCode={undefined}
text="with exit code"
/>
</TestProviders>
);
expect(wrapper.text()).toEqual('');
});
test('it renders just the exit code when text is undefined', () => {
test('it renders just the endgameExitCode code when text is undefined', () => {
const wrapper = mount(
<TestProviders>
<ExitCodeDraggable contextId="test" endgameExitCode="1" eventId="1" text={undefined} />
<ExitCodeDraggable
contextId="test"
endgameExitCode="1"
eventId="1"
processExitCode={undefined}
text={undefined}
/>
</TestProviders>
);
expect(wrapper.text()).toEqual('1');
});
test('it renders just the exit code when text is null', () => {
test('it renders just the processExitCode code when text is undefined', () => {
const wrapper = mount(
<TestProviders>
<ExitCodeDraggable contextId="test" endgameExitCode="1" eventId="1" text={null} />
<ExitCodeDraggable
contextId="test"
endgameExitCode={undefined}
eventId="1"
processExitCode={-1}
text={undefined}
/>
</TestProviders>
);
expect(wrapper.text()).toEqual('-1');
});
test('it renders just the endgameExitCode code when text is null', () => {
const wrapper = mount(
<TestProviders>
<ExitCodeDraggable
contextId="test"
endgameExitCode="1"
eventId="1"
processExitCode={undefined}
text={null}
/>
</TestProviders>
);
expect(wrapper.text()).toEqual('1');
});
test('it renders just the exit code when text is an empty string', () => {
test('it renders just the processExitCode code when text is null', () => {
const wrapper = mount(
<TestProviders>
<ExitCodeDraggable contextId="test" endgameExitCode="1" eventId="1" text="" />
<ExitCodeDraggable
contextId="test"
endgameExitCode={undefined}
eventId="1"
processExitCode={-1}
text={null}
/>
</TestProviders>
);
expect(wrapper.text()).toEqual('-1');
});
test('it renders just the endgameExitCode code when text is an empty string', () => {
const wrapper = mount(
<TestProviders>
<ExitCodeDraggable
contextId="test"
endgameExitCode="1"
eventId="1"
processExitCode={undefined}
text=""
/>
</TestProviders>
);
expect(wrapper.text()).toEqual('1');
});
test('it renders just the processExitCode code when text is an empty string', () => {
const wrapper = mount(
<TestProviders>
<ExitCodeDraggable
contextId="test"
endgameExitCode={undefined}
eventId="1"
processExitCode={-1}
text=""
/>
</TestProviders>
);
expect(wrapper.text()).toEqual('-1');
});
});

View file

@ -15,12 +15,13 @@ interface Props {
contextId: string;
endgameExitCode: string | null | undefined;
eventId: string;
processExitCode: number | null | undefined;
text: string | null | undefined;
}
export const ExitCodeDraggable = React.memo<Props>(
({ contextId, endgameExitCode, eventId, text }) => {
if (isNillEmptyOrNotFinite(endgameExitCode)) {
({ contextId, endgameExitCode, eventId, processExitCode, text }) => {
if (isNillEmptyOrNotFinite(processExitCode) && isNillEmptyOrNotFinite(endgameExitCode)) {
return null;
}
@ -32,14 +33,27 @@ export const ExitCodeDraggable = React.memo<Props>(
</TokensFlexItem>
)}
<TokensFlexItem grow={false} component="span">
<DraggableBadge
contextId={contextId}
eventId={eventId}
field="endgame.exit_code"
value={endgameExitCode}
/>
</TokensFlexItem>
{!isNillEmptyOrNotFinite(processExitCode) && (
<TokensFlexItem grow={false} component="span">
<DraggableBadge
contextId={contextId}
eventId={eventId}
field="process.exit_code"
value={`${processExitCode}`}
/>
</TokensFlexItem>
)}
{!isNillEmptyOrNotFinite(endgameExitCode) && (
<TokensFlexItem grow={false} component="span">
<DraggableBadge
contextId={contextId}
eventId={eventId}
field="endgame.exit_code"
value={endgameExitCode}
/>
</TokensFlexItem>
)}
</>
);
}

View file

@ -51,14 +51,15 @@ export const isFileEvent = ({
eventCategory: string | null | undefined;
eventDataset: string | null | undefined;
}) =>
(eventCategory != null && eventCategory.toLowerCase() === 'file') ||
(eventDataset != null && eventDataset.toLowerCase() === 'file');
eventCategory?.toLowerCase() === 'file' ||
eventDataset?.toLowerCase() === 'file' ||
eventDataset?.toLowerCase() === 'endpoint.events.file';
export const isProcessStoppedOrTerminationEvent = (
eventAction: string | null | undefined
): boolean => ['process_stopped', 'termination_event'].includes(`${eventAction}`.toLowerCase());
export const showVia = (eventAction: string | null | undefined): boolean =>
['file_create_event', 'created', 'file_delete_event', 'deleted'].includes(
['file_create_event', 'created', 'creation', 'file_delete_event', 'deleted', 'deletion'].includes(
`${eventAction}`.toLowerCase()
);

View file

@ -25,28 +25,34 @@ jest.mock('@elastic/eui', () => {
describe('ParentProcessDraggable', () => {
const mount = useMountAppended();
test('displays the text, endgameParentProcessName, and processPpid when they are all provided', () => {
test('displays the text, endgameParentProcessName, processParentName, processParentPid, and processPpid when they are all provided', () => {
const wrapper = mount(
<TestProviders>
<ParentProcessDraggable
contextId="test"
endgameParentProcessName="[endgameParentProcessName]"
eventId="1"
processParentName="[processParentName]"
processParentPid={789}
processPpid={456}
text="via parent process"
/>
</TestProviders>
);
expect(wrapper.text()).toEqual('via parent process[endgameParentProcessName](456)');
expect(wrapper.text()).toEqual(
'via parent process[processParentName][endgameParentProcessName](789)(456)'
);
});
test('displays nothing when the text is provided, but endgameParentProcessName and processPpid are both undefined', () => {
test('displays nothing when the text is provided, but endgameParentProcessName and processParentName are both undefined', () => {
const wrapper = mount(
<TestProviders>
<ParentProcessDraggable
contextId="test"
endgameParentProcessName={undefined}
eventId="1"
processParentName={undefined}
processParentPid={undefined}
processPpid={undefined}
text="via parent process"
/>
@ -55,36 +61,6 @@ describe('ParentProcessDraggable', () => {
expect(wrapper.text()).toEqual('');
});
test('displays the text and processPpid when endgameParentProcessName is undefined', () => {
const wrapper = mount(
<TestProviders>
<ParentProcessDraggable
contextId="test"
endgameParentProcessName={undefined}
eventId="1"
processPpid={456}
text="via parent process"
/>
</TestProviders>
);
expect(wrapper.text()).toEqual('via parent process(456)');
});
test('displays the processPpid when both endgameParentProcessName and text are undefined', () => {
const wrapper = mount(
<TestProviders>
<ParentProcessDraggable
contextId="test"
endgameParentProcessName={undefined}
eventId="1"
processPpid={456}
text={undefined}
/>
</TestProviders>
);
expect(wrapper.text()).toEqual('(456)');
});
test('displays the text and endgameParentProcessName when processPpid is undefined', () => {
const wrapper = mount(
<TestProviders>
@ -92,6 +68,8 @@ describe('ParentProcessDraggable', () => {
contextId="test"
endgameParentProcessName="[endgameParentProcessName]"
eventId="1"
processParentName={undefined}
processParentPid={undefined}
processPpid={undefined}
text="via parent process"
/>
@ -100,6 +78,23 @@ describe('ParentProcessDraggable', () => {
expect(wrapper.text()).toEqual('via parent process[endgameParentProcessName]');
});
test('displays the text and processParentName when processParentPid is undefined', () => {
const wrapper = mount(
<TestProviders>
<ParentProcessDraggable
contextId="test"
endgameParentProcessName={undefined}
eventId="1"
processParentName="[processParentName]"
processParentPid={undefined}
processPpid={undefined}
text="via parent process"
/>
</TestProviders>
);
expect(wrapper.text()).toEqual('via parent process[processParentName]');
});
test('displays the endgameParentProcessName when both processPpid and text are undefined', () => {
const wrapper = mount(
<TestProviders>
@ -107,6 +102,8 @@ describe('ParentProcessDraggable', () => {
contextId="test"
endgameParentProcessName="[endgameParentProcessName]"
eventId="1"
processParentName={undefined}
processParentPid={undefined}
processPpid={undefined}
text={undefined}
/>
@ -114,4 +111,21 @@ describe('ParentProcessDraggable', () => {
);
expect(wrapper.text()).toEqual('[endgameParentProcessName]');
});
test('displays the processParentName when both processParentPid and text are undefined', () => {
const wrapper = mount(
<TestProviders>
<ParentProcessDraggable
contextId="test"
endgameParentProcessName={undefined}
eventId="1"
processParentName="[processParentName]"
processParentPid={undefined}
processPpid={undefined}
text={undefined}
/>
</TestProviders>
);
expect(wrapper.text()).toEqual('[processParentName]');
});
});

View file

@ -15,13 +15,26 @@ interface Props {
contextId: string;
endgameParentProcessName: string | null | undefined;
eventId: string;
processParentPid: number | null | undefined;
processParentName: string | null | undefined;
processPpid: number | undefined | null;
text: string | null | undefined;
}
export const ParentProcessDraggable = React.memo<Props>(
({ contextId, endgameParentProcessName, eventId, processPpid, text }) => {
if (isNillEmptyOrNotFinite(endgameParentProcessName) && isNillEmptyOrNotFinite(processPpid)) {
({
contextId,
endgameParentProcessName,
eventId,
processParentName,
processParentPid,
processPpid,
text,
}) => {
if (
isNillEmptyOrNotFinite(processParentName) &&
isNillEmptyOrNotFinite(endgameParentProcessName)
) {
return null;
}
@ -37,6 +50,17 @@ export const ParentProcessDraggable = React.memo<Props>(
</TokensFlexItem>
)}
{!isNillEmptyOrNotFinite(processParentName) && (
<TokensFlexItem grow={false} component="span">
<DraggableBadge
contextId={contextId}
eventId={eventId}
field="process.parent.name"
value={processParentName}
/>
</TokensFlexItem>
)}
{!isNillEmptyOrNotFinite(endgameParentProcessName) && (
<TokensFlexItem grow={false} component="span">
<DraggableBadge
@ -48,6 +72,18 @@ export const ParentProcessDraggable = React.memo<Props>(
</TokensFlexItem>
)}
{!isNillEmptyOrNotFinite(processParentPid) && (
<TokensFlexItem grow={false} component="span">
<DraggableBadge
contextId={contextId}
eventId={eventId}
field="process.parent.pid"
queryValue={String(processParentPid)}
value={`(${String(processParentPid)})`}
/>
</TokensFlexItem>
)}
{!isNillEmptyOrNotFinite(processPpid) && (
<TokensFlexItem grow={false} component="span">
<DraggableBadge

View file

@ -5,6 +5,7 @@
* 2.0.
*/
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import React from 'react';
import { DraggableBadge } from '../../../../../common/components/draggables';
@ -43,51 +44,61 @@ export const ProcessDraggable = React.memo<Props>(
}
return (
<div>
<EuiFlexGroup alignItems="center" gutterSize="none">
{!isNillEmptyOrNotFinite(processName) ? (
<DraggableBadge
contextId={contextId}
eventId={eventId}
field="process.name"
value={processName}
iconType="console"
/>
<EuiFlexItem grow={false}>
<DraggableBadge
contextId={contextId}
eventId={eventId}
field="process.name"
value={processName}
iconType="console"
/>
</EuiFlexItem>
) : !isNillEmptyOrNotFinite(processExecutable) ? (
<DraggableBadge
contextId={contextId}
eventId={eventId}
field="process.executable"
value={processExecutable}
iconType="console"
/>
<EuiFlexItem grow={false}>
<DraggableBadge
contextId={contextId}
eventId={eventId}
field="process.executable"
value={processExecutable}
iconType="console"
/>
</EuiFlexItem>
) : !isNillEmptyOrNotFinite(endgameProcessName) ? (
<DraggableBadge
contextId={contextId}
eventId={eventId}
field="endgame.process_name"
value={endgameProcessName}
iconType="console"
/>
<EuiFlexItem grow={false}>
<DraggableBadge
contextId={contextId}
eventId={eventId}
field="endgame.process_name"
value={endgameProcessName}
iconType="console"
/>
</EuiFlexItem>
) : null}
{!isNillEmptyOrNotFinite(processPid) ? (
<DraggableBadge
contextId={contextId}
eventId={eventId}
field="process.pid"
queryValue={String(processPid)}
value={`(${String(processPid)})`}
/>
<EuiFlexItem grow={false}>
<DraggableBadge
contextId={contextId}
eventId={eventId}
field="process.pid"
queryValue={String(processPid)}
value={`(${String(processPid)})`}
/>
</EuiFlexItem>
) : !isNillEmptyOrNotFinite(endgamePid) ? (
<DraggableBadge
contextId={contextId}
eventId={eventId}
field="endgame.pid"
queryValue={String(endgamePid)}
value={`(${String(endgamePid)})`}
/>
<EuiFlexItem grow={false}>
<DraggableBadge
contextId={contextId}
eventId={eventId}
field="endgame.pid"
queryValue={String(endgamePid)}
value={`(${String(endgamePid)})`}
/>
</EuiFlexItem>
) : null}
</div>
</EuiFlexGroup>
);
}
);

View file

@ -48,6 +48,9 @@ interface Props {
packageSummary: string | null | undefined;
packageVersion: string | null | undefined;
processName: string | null | undefined;
processParentName: string | null | undefined;
processParentPid: number | null | undefined;
processExitCode: number | null | undefined;
processPid: number | null | undefined;
processPpid: number | null | undefined;
processExecutable: string | null | undefined;
@ -82,6 +85,9 @@ export const SystemGenericFileLine = React.memo<Props>(
message,
outcome,
packageName,
processParentName,
processParentPid,
processExitCode,
packageSummary,
packageVersion,
processExecutable,
@ -142,6 +148,7 @@ export const SystemGenericFileLine = React.memo<Props>(
contextId={contextId}
endgameExitCode={endgameExitCode}
eventId={id}
processExitCode={processExitCode}
text={i18n.WITH_EXIT_CODE}
/>
{!isProcessStoppedOrTerminationEvent(eventAction) && (
@ -149,6 +156,8 @@ export const SystemGenericFileLine = React.memo<Props>(
contextId={contextId}
endgameParentProcessName={endgameParentProcessName}
eventId={id}
processParentName={processParentName}
processParentPid={processParentPid}
processPpid={processPpid}
text={i18n.VIA_PARENT_PROCESS}
/>
@ -239,6 +248,9 @@ export const SystemGenericFileDetails = React.memo<GenericDetailsProps>(
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 processExitCode: number | null | undefined = get('process.exit_code[0]', data);
const processParentName: string | null | undefined = get('process.parent.name[0]', data);
const processParentPid: number | null | undefined = get('process.parent.pid[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[0]', data);
@ -271,6 +283,9 @@ export const SystemGenericFileDetails = React.memo<GenericDetailsProps>(
userDomain={userDomain}
userName={userName}
message={message}
processExitCode={processExitCode}
processParentName={processParentName}
processParentPid={processParentPid}
processTitle={processTitle}
workingDirectory={workingDirectory}
args={args}

View file

@ -36,6 +36,17 @@ import {
mockEndgameTerminationEvent,
mockEndgameUserLogoff,
mockEndgameUserLogon,
mockEndpointDisconnectReceivedEvent,
mockEndpointFileCreationEvent,
mockEndpointFileDeletionEvent,
mockEndpointNetworkConnectionAcceptedEvent,
mockEndpointNetworkLookupRequestedEvent,
mockEndpointNetworkLookupResultEvent,
mockEndpointProcessStartEvent,
mockEndpointProcessEndEvent,
mockEndpointSecurityLogOnSuccessEvent,
mockEndpointSecurityLogOnFailureEvent,
mockEndpointSecurityLogOffEvent,
} from '../../../../../../common/mock/mock_endgame_ecs_data';
import { useMountAppended } from '../../../../../../common/utils/use_mount_appended';
import { RowRenderer } from '../row_renderer';
@ -187,6 +198,31 @@ describe('GenericRowRenderer', () => {
});
describe('#createEndgameProcessRowRenderer', () => {
test('it renders an endpoint process start event', () => {
const actionName = 'start';
const text = i18n.PROCESS_STARTED;
const endpointProcessStartRowRenderer = createEndgameProcessRowRenderer({
actionName,
text,
});
const wrapper = mount(
<TestProviders>
{endpointProcessStartRowRenderer.isInstance(mockEndpointProcessStartEvent) &&
endpointProcessStartRowRenderer.renderRow({
browserFields: mockBrowserFields,
data: mockEndpointProcessStartEvent,
timelineId: 'test',
})}
</TestProviders>
);
expect(wrapper.text()).toEqual(
'SYSTEM\\NT AUTHORITY@win2019-endpoint-1started processconhost.exe(3636)C:\\Windows\\system32\\conhost.exe,0xffffffff,-ForceV1697334c236cce7d4c9e223146ee683a1219adced9729d4ae771fd6a1502a6b63e19da2c35ba1c38adf12d1a472c1fcf1f1a811a71b0e9b5fcb62de0787235ecca560b610'
);
});
test('it renders an endgame process creation_event', () => {
const actionName = 'creation_event';
const text = i18n.PROCESS_STARTED;
@ -215,6 +251,31 @@ describe('GenericRowRenderer', () => {
);
});
test('it renders an endpoint process end event', () => {
const actionName = 'end';
const text = i18n.TERMINATED_PROCESS;
const endpointProcessEndRowRenderer = createEndgameProcessRowRenderer({
actionName,
text,
});
const wrapper = mount(
<TestProviders>
{endpointProcessEndRowRenderer.isInstance(mockEndpointProcessEndEvent) &&
endpointProcessEndRowRenderer.renderRow({
browserFields: mockBrowserFields,
data: mockEndpointProcessEndEvent,
timelineId: 'test',
})}
</TestProviders>
);
expect(wrapper.text()).toEqual(
'SYSTEM\\NT AUTHORITY@win2019-endpointterminated processsvchost.exe(10392)C:\\Windows\\System32\\svchost.exe,-k,netsvcs,-p,-s,NetSetupSvcwith exit code-1via parent processservices.exe7fd065bac18c5278777ae44908101cdfed72d26fa741367f0ad4d02020787ab6a1385ce20ad79f55df235effd9780c31442aa2348a0a29438052faed8a2532da50455756'
);
});
test('it renders an endgame process termination_event', () => {
const actionName = 'termination_event';
const text = i18n.TERMINATED_PROCESS;
@ -331,6 +392,31 @@ describe('GenericRowRenderer', () => {
});
describe('#createFimRowRenderer', () => {
test('it renders an endpoint file creation event', () => {
const actionName = 'creation';
const text = i18n.CREATED_FILE;
const endpointFileCreationRowRenderer = createFimRowRenderer({
actionName,
text,
});
const wrapper = mount(
<TestProviders>
{endpointFileCreationRowRenderer.isInstance(mockEndpointFileCreationEvent) &&
endpointFileCreationRowRenderer.renderRow({
browserFields: mockBrowserFields,
data: mockEndpointFileCreationEvent,
timelineId: 'test',
})}
</TestProviders>
);
expect(wrapper.text()).toEqual(
'SYSTEM\\NT AUTHORITY@win2019-endpointcreated a fileWimProvider.dllinC:\\Windows\\TEMP\\E38FD162-B6E6-4799-B52D-F590BACBAE94\\WimProvider.dllviaMsMpEng.exe(2424)'
);
});
test('it renders an endgame file_create_event', () => {
const actionName = 'file_create_event';
const text = i18n.CREATED_FILE;
@ -359,6 +445,31 @@ describe('GenericRowRenderer', () => {
);
});
test('it renders an endpoint file deletion event', () => {
const actionName = 'deletion';
const text = i18n.DELETED_FILE;
const endpointFileDeletionRowRenderer = createFimRowRenderer({
actionName,
text,
});
const wrapper = mount(
<TestProviders>
{endpointFileDeletionRowRenderer.isInstance(mockEndpointFileDeletionEvent) &&
endpointFileDeletionRowRenderer.renderRow({
browserFields: mockBrowserFields,
data: mockEndpointFileDeletionEvent,
timelineId: 'test',
})}
</TestProviders>
);
expect(wrapper.text()).toEqual(
'SYSTEM\\NT AUTHORITY@windows-endpoint-1deleted a fileAM_Delta_Patch_1.329.2793.0.exeinC:\\Windows\\SoftwareDistribution\\Download\\Install\\AM_Delta_Patch_1.329.2793.0.exeviasvchost.exe(1728)'
);
});
test('it renders an endgame file_delete_event', () => {
const actionName = 'file_delete_event';
const text = i18n.DELETED_FILE;
@ -529,6 +640,33 @@ describe('GenericRowRenderer', () => {
});
describe('#createSocketRowRenderer', () => {
test('it renders an Endpoint network connection_accepted event', () => {
const actionName = 'connection_accepted';
const text = i18n.ACCEPTED_A_CONNECTION_VIA;
const endpointConnectionAcceptedRowRenderer = createSocketRowRenderer({
actionName,
text,
});
const wrapper = mount(
<TestProviders>
{endpointConnectionAcceptedRowRenderer.isInstance(
mockEndpointNetworkConnectionAcceptedEvent
) &&
endpointConnectionAcceptedRowRenderer.renderRow({
browserFields: mockBrowserFields,
data: mockEndpointNetworkConnectionAcceptedEvent,
timelineId: 'test',
})}
</TestProviders>
);
expect(removeExternalLinkText(wrapper.text())).toEqual(
'NETWORK SERVICE\\NT AUTHORITY@windows-endpoint-1accepted a connection viasvchost.exe(328)with resultsuccessEndpoint network eventincomingtcpSource10.1.2.3:64557North AmericaUnited States🇺🇸USNorth CarolinaConcordDestination10.50.60.70:3389'
);
});
test('it renders an Endgame ipv4_connection_accept_event', () => {
const actionName = 'ipv4_connection_accept_event';
const text = i18n.ACCEPTED_A_CONNECTION_VIA;
@ -585,6 +723,31 @@ describe('GenericRowRenderer', () => {
);
});
test('it renders an endpoint network disconnect_received event', () => {
const actionName = 'disconnect_received';
const text = i18n.DISCONNECTED_VIA;
const endpointDisconnectReceivedRowRenderer = createSocketRowRenderer({
actionName,
text,
});
const wrapper = mount(
<TestProviders>
{endpointDisconnectReceivedRowRenderer.isInstance(mockEndpointDisconnectReceivedEvent) &&
endpointDisconnectReceivedRowRenderer.renderRow({
browserFields: mockBrowserFields,
data: mockEndpointDisconnectReceivedEvent,
timelineId: 'test',
})}
</TestProviders>
);
expect(removeExternalLinkText(wrapper.text())).toEqual(
'NETWORK SERVICE\\NT AUTHORITY@windows-endpoint-1disconnected viasvchost.exe(328)Endpoint network eventincomingtcpSource10.20.30.40:64557North AmericaUnited States🇺🇸USNorth CarolinaConcord(42.47%)1.2KB(57.53%)1.6KBDestination10.11.12.13:3389'
);
});
test('it renders an Endgame ipv4_disconnect_received_event', () => {
const actionName = 'ipv4_disconnect_received_event';
const text = i18n.DISCONNECTED_VIA;
@ -725,6 +888,48 @@ describe('GenericRowRenderer', () => {
});
describe('#createSecurityEventRowRenderer', () => {
test('it renders an endpoint security log_on event with event.outcome: success', () => {
const actionName = 'log_on';
const securityLogOnRowRenderer = createSecurityEventRowRenderer({ actionName });
const wrapper = mount(
<TestProviders>
{securityLogOnRowRenderer.isInstance(mockEndpointSecurityLogOnSuccessEvent) &&
securityLogOnRowRenderer.renderRow({
browserFields: mockBrowserFields,
data: mockEndpointSecurityLogOnSuccessEvent,
timelineId: 'test',
})}
</TestProviders>
);
expect(wrapper.text()).toEqual(
'SYSTEM\\NT AUTHORITY@win2019-endpointsuccessfully logged inviaC:\\Program Files\\OpenSSH-Win64\\sshd.exe(90210)'
);
});
test('it renders an endpoint security log_on event with event.outcome: failure', () => {
const actionName = 'log_on';
const securityLogOnRowRenderer = createSecurityEventRowRenderer({ actionName });
const wrapper = mount(
<TestProviders>
{securityLogOnRowRenderer.isInstance(mockEndpointSecurityLogOnFailureEvent) &&
securityLogOnRowRenderer.renderRow({
browserFields: mockBrowserFields,
data: mockEndpointSecurityLogOnFailureEvent,
timelineId: 'test',
})}
</TestProviders>
);
expect(wrapper.text()).toEqual(
'win2019-endpointfailed to log inviaC:\\Program Files\\OpenSSH-Win64\\sshd.exe(90210)'
);
});
test('it renders an Endgame user_logon event', () => {
const actionName = 'user_logon';
const userLogonEvent = {
@ -797,6 +1002,27 @@ describe('GenericRowRenderer', () => {
);
});
test('it renders an endpoint security log_off event', () => {
const actionName = 'log_off';
const securityLogOffRowRenderer = createSecurityEventRowRenderer({ actionName });
const wrapper = mount(
<TestProviders>
{securityLogOffRowRenderer.isInstance(mockEndpointSecurityLogOffEvent) &&
securityLogOffRowRenderer.renderRow({
browserFields: mockBrowserFields,
data: mockEndpointSecurityLogOffEvent,
timelineId: 'test',
})}
</TestProviders>
);
expect(wrapper.text()).toEqual(
'SYSTEM\\NT AUTHORITY@win2019-endpointlogged offviaC:\\Windows\\System32\\lsass.exe(90210)'
);
});
test('it renders an Endgame user_logoff event', () => {
const actionName = 'user_logoff';
const userLogoffEvent = {
@ -845,6 +1071,44 @@ describe('GenericRowRenderer', () => {
});
describe('#createDnsRowRenderer', () => {
test('it renders an endpoint network lookup_requested event', () => {
const dnsRowRenderer = createDnsRowRenderer();
const wrapper = mount(
<TestProviders>
{dnsRowRenderer.isInstance(mockEndpointNetworkLookupRequestedEvent) &&
dnsRowRenderer.renderRow({
browserFields: mockBrowserFields,
data: mockEndpointNetworkLookupRequestedEvent,
timelineId: 'test',
})}
</TestProviders>
);
expect(wrapper.text()).toEqual(
'SYSTEM\\NT AUTHORITY@win2019-endpointasked forlogging.googleapis.comwith question typeAviagoogle_osconfig_agent.exe(3272)dns'
);
});
test('it renders an endpoint network lookup_result event', () => {
const dnsRowRenderer = createDnsRowRenderer();
const wrapper = mount(
<TestProviders>
{dnsRowRenderer.isInstance(mockEndpointNetworkLookupResultEvent) &&
dnsRowRenderer.renderRow({
browserFields: mockBrowserFields,
data: mockEndpointNetworkLookupResultEvent,
timelineId: 'test',
})}
</TestProviders>
);
expect(wrapper.text()).toEqual(
'SYSTEM\\NT AUTHORITY@win2019-endpointasked forlogging.googleapis.comwith question typeAAAAviagoogle_osconfig_agent.exe(3272)dns'
);
});
test('it renders an Endgame DNS request_event', () => {
const requestEvent = {
...mockEndgameDnsRequest,

View file

@ -63,11 +63,11 @@ export const createEndgameProcessRowRenderer = ({
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 (
category != null &&
category.toLowerCase() === 'process' &&
action != null &&
action.toLowerCase() === actionName
(category?.toLowerCase() === 'process' ||
dataset?.toLowerCase() === 'endpoint.events.process') &&
action?.toLowerCase() === actionName
);
},
renderRow: ({ browserFields, data, timelineId }) => (
@ -98,8 +98,7 @@ export const createFimRowRenderer = ({
const dataset: string | null | undefined = get('event.dataset[0]', ecs);
return (
isFileEvent({ eventCategory: category, eventDataset: dataset }) &&
action != null &&
action.toLowerCase() === actionName
action?.toLowerCase() === actionName
);
},
renderRow: ({ browserFields, data, timelineId }) => (
@ -181,11 +180,11 @@ export const createSecurityEventRowRenderer = ({
isInstance: (ecs) => {
const category: string | null | undefined = get('event.category[0]', ecs);
const action: string | null | undefined = get('event.action[0]', ecs);
const dataset: string | null | undefined = get('event.dataset[0]', ecs);
return (
category != null &&
category.toLowerCase() === 'authentication' &&
action != null &&
action.toLowerCase() === actionName
(category?.toLowerCase() === 'authentication' ||
dataset?.toLowerCase() === 'endpoint.events.security') &&
action?.toLowerCase() === actionName
);
},
renderRow: ({ browserFields, data, timelineId }) => (
@ -234,6 +233,11 @@ const endgameProcessStartedRowRenderer = createEndgameProcessRowRenderer({
text: i18n.PROCESS_STARTED,
});
const endpointProcessStartRowRenderer = createEndgameProcessRowRenderer({
actionName: 'start',
text: i18n.PROCESS_STARTED,
});
const systemProcessStoppedRowRenderer = createGenericFileRowRenderer({
actionName: 'process_stopped',
text: i18n.PROCESS_STOPPED,
@ -244,11 +248,21 @@ const endgameProcessTerminationRowRenderer = createEndgameProcessRowRenderer({
text: i18n.TERMINATED_PROCESS,
});
const endpointProcessEndRowRenderer = createEndgameProcessRowRenderer({
actionName: 'end',
text: i18n.TERMINATED_PROCESS,
});
const endgameFileCreateEventRowRenderer = createFimRowRenderer({
actionName: 'file_create_event',
text: i18n.CREATED_FILE,
});
const endpointFileCreationEventRowRenderer = createFimRowRenderer({
actionName: 'creation',
text: i18n.CREATED_FILE,
});
const fimFileCreateEventRowRenderer = createFimRowRenderer({
actionName: 'created',
text: i18n.CREATED_FILE,
@ -259,6 +273,11 @@ const endgameFileDeleteEventRowRenderer = createFimRowRenderer({
text: i18n.DELETED_FILE,
});
const endpointFileDeletionEventRowRenderer = createFimRowRenderer({
actionName: 'deletion',
text: i18n.DELETED_FILE,
});
const fimFileDeletedEventRowRenderer = createFimRowRenderer({
actionName: 'deleted',
text: i18n.DELETED_FILE,
@ -284,6 +303,11 @@ const endgameIpv4ConnectionAcceptEventRowRenderer = createSocketRowRenderer({
text: i18n.ACCEPTED_A_CONNECTION_VIA,
});
const endpointConnectionAcceptedEventRowRenderer = createSocketRowRenderer({
actionName: 'connection_accepted',
text: i18n.ACCEPTED_A_CONNECTION_VIA,
});
const endgameIpv6ConnectionAcceptEventRowRenderer = createSocketRowRenderer({
actionName: 'ipv6_connection_accept_event',
text: i18n.ACCEPTED_A_CONNECTION_VIA,
@ -294,6 +318,11 @@ const endgameIpv4DisconnectReceivedEventRowRenderer = createSocketRowRenderer({
text: i18n.DISCONNECTED_VIA,
});
const endpointDisconnectReceivedEventRowRenderer = createSocketRowRenderer({
actionName: 'disconnect_received',
text: i18n.DISCONNECTED_VIA,
});
const endgameIpv6DisconnectReceivedEventRowRenderer = createSocketRowRenderer({
actionName: 'ipv6_disconnect_received_event',
text: i18n.DISCONNECTED_VIA,
@ -315,6 +344,10 @@ const endgameUserLogonRowRenderer = createSecurityEventRowRenderer({
actionName: 'user_logon',
});
const endpointUserLogOnRowRenderer = createSecurityEventRowRenderer({
actionName: 'log_on',
});
const dnsRowRenderer = createDnsRowRenderer();
const systemExistingUserRowRenderer = createGenericSystemRowRenderer({
@ -357,6 +390,10 @@ const systemLogoutRowRenderer = createGenericSystemRowRenderer({
text: i18n.LOGGED_OUT,
});
const endpointUserLogOffRowRenderer = createSecurityEventRowRenderer({
actionName: 'log_off',
});
const systemProcessErrorRowRenderer = createGenericFileRowRenderer({
actionName: 'process_error',
text: i18n.PROCESS_ERROR,
@ -408,15 +445,22 @@ export const systemRowRenderers: RowRenderer[] = [
endgameAdminLogonRowRenderer,
endgameExplicitUserLogonRowRenderer,
endgameFileCreateEventRowRenderer,
endpointFileCreationEventRowRenderer,
endgameFileDeleteEventRowRenderer,
endpointFileDeletionEventRowRenderer,
endgameIpv4ConnectionAcceptEventRowRenderer,
endpointConnectionAcceptedEventRowRenderer,
endgameIpv6ConnectionAcceptEventRowRenderer,
endgameIpv4DisconnectReceivedEventRowRenderer,
endpointDisconnectReceivedEventRowRenderer,
endgameIpv6DisconnectReceivedEventRowRenderer,
endgameProcessStartedRowRenderer,
endpointProcessStartRowRenderer,
endgameProcessTerminationRowRenderer,
endpointProcessEndRowRenderer,
endgameUserLogoffRowRenderer,
endgameUserLogonRowRenderer,
endpointUserLogOnRowRenderer,
fimFileCreateEventRowRenderer,
fimFileDeletedEventRowRenderer,
systemAcceptedRowRenderer,
@ -431,6 +475,7 @@ export const systemRowRenderers: RowRenderer[] = [
systemInvalidRowRenderer,
systemLoginRowRenderer,
systemLogoutRowRenderer,
endpointUserLogOffRowRenderer,
systemPackageInstalledRowRenderer,
systemPackageUpdatedRowRenderer,
systemPackageRemovedRowRenderer,

View file

@ -162,9 +162,12 @@ export const TIMELINE_EVENTS_FIELDS = [
'tls.server_certificate.fingerprint.sha1',
'user.domain',
'winlog.event_id',
'process.exit_code',
'process.hash.md5',
'process.hash.sha1',
'process.hash.sha256',
'process.parent.name',
'process.parent.pid',
'process.pid',
'process.name',
'process.ppid',