diff --git a/x-pack/plugins/security_solution/common/endpoint/data_generators/base_data_generator.ts b/x-pack/plugins/security_solution/common/endpoint/data_generators/base_data_generator.ts index c0888a6c2a4bd..35c976fbdfb1d 100644 --- a/x-pack/plugins/security_solution/common/endpoint/data_generators/base_data_generator.ts +++ b/x-pack/plugins/security_solution/common/endpoint/data_generators/base_data_generator.ts @@ -9,6 +9,8 @@ import seedrandom from 'seedrandom'; import uuid from 'uuid'; const OS_FAMILY = ['windows', 'macos', 'linux']; +/** Array of 14 day offsets */ +const DAY_OFFSETS = Array.from({ length: 14 }, (_, i) => 8.64e7 * (i + 1)); /** * A generic base class to assist in creating domain specific data generators. It includes @@ -16,6 +18,7 @@ const OS_FAMILY = ['windows', 'macos', 'linux']; * public method named `generate()` which should be implemented by sub-classes. */ export class BaseDataGenerator { + /** A javascript seeded random number (float between 0 and 1). Don't use `Math.random()` */ protected random: seedrandom.prng; constructor(seed: string | seedrandom.prng = Math.random().toString()) { @@ -33,6 +36,23 @@ export class BaseDataGenerator { throw new Error('method not implemented!'); } + /** Returns a future ISO date string */ + protected randomFutureDate(from?: Date): string { + const now = from ? from.getTime() : Date.now(); + return new Date(now + this.randomChoice(DAY_OFFSETS)).toISOString(); + } + + /** Returns a past ISO date string */ + protected randomPastDate(from?: Date): string { + const now = from ? from.getTime() : Date.now(); + return new Date(now - this.randomChoice(DAY_OFFSETS)).toISOString(); + } + + /** Generate either `true` or `false` */ + protected randomBoolean(): boolean { + return this.random() < 0.5; + } + /** generate random OS family value */ protected randomOSFamily(): string { return this.randomChoice(OS_FAMILY); diff --git a/x-pack/plugins/security_solution/common/endpoint/data_generators/fleet_action_generator.ts b/x-pack/plugins/security_solution/common/endpoint/data_generators/fleet_action_generator.ts new file mode 100644 index 0000000000000..af799de782f48 --- /dev/null +++ b/x-pack/plugins/security_solution/common/endpoint/data_generators/fleet_action_generator.ts @@ -0,0 +1,62 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { DeepPartial } from 'utility-types'; +import { merge } from 'lodash'; +import { BaseDataGenerator } from './base_data_generator'; +import { EndpointAction, EndpointActionResponse, ISOLATION_ACTIONS } from '../types'; + +const ISOLATION_COMMANDS: ISOLATION_ACTIONS[] = ['isolate', 'unisolate']; + +export class FleetActionGenerator extends BaseDataGenerator { + /** Generate an Action */ + generate(overrides: DeepPartial = {}): EndpointAction { + const timeStamp = new Date(this.randomPastDate()); + + return merge( + { + action_id: this.randomUUID(), + '@timestamp': timeStamp.toISOString(), + expiration: this.randomFutureDate(timeStamp), + type: 'INPUT_ACTION', + input_type: 'endpoint', + agents: [this.randomUUID()], + user_id: 'elastic', + data: { + command: this.randomIsolateCommand(), + comment: this.randomString(15), + }, + }, + overrides + ); + } + + /** Generates an action response */ + generateResponse(overrides: DeepPartial = {}): EndpointActionResponse { + const timeStamp = new Date(); + + return merge( + { + action_data: { + command: this.randomIsolateCommand(), + comment: '', + }, + action_id: this.randomUUID(), + agent_id: this.randomUUID(), + started_at: this.randomPastDate(), + completed_at: timeStamp.toISOString(), + error: 'some error happen', + '@timestamp': timeStamp.toISOString(), + }, + overrides + ); + } + + protected randomIsolateCommand() { + return this.randomChoice(ISOLATION_COMMANDS); + } +} diff --git a/x-pack/plugins/security_solution/common/endpoint/generate_data.test.ts b/x-pack/plugins/security_solution/common/endpoint/generate_data.test.ts index e29a121668bd3..301a032fb47df 100644 --- a/x-pack/plugins/security_solution/common/endpoint/generate_data.test.ts +++ b/x-pack/plugins/security_solution/common/endpoint/generate_data.test.ts @@ -87,7 +87,9 @@ describe('data generator', () => { expect(event2.event?.sequence).toBe((firstNonNullValue(event1.event?.sequence) ?? 0) + 1); }); - it('creates the same documents with same random seed', () => { + // Lets run this one multiple times just to ensure that the randomness + // is truly predicable based on the seed passed + it.each([1, 2, 3, 4, 5])('[%#] creates the same documents with same random seed', () => { const generator1 = new EndpointDocGenerator('seed'); const generator2 = new EndpointDocGenerator('seed'); const timestamp = new Date().getTime(); diff --git a/x-pack/plugins/security_solution/common/endpoint/generate_data.ts b/x-pack/plugins/security_solution/common/endpoint/generate_data.ts index fa7ee84441a9b..436f1573639c8 100644 --- a/x-pack/plugins/security_solution/common/endpoint/generate_data.ts +++ b/x-pack/plugins/security_solution/common/endpoint/generate_data.ts @@ -439,6 +439,8 @@ export class EndpointDocGenerator extends BaseDataGenerator { private createHostData(): HostInfo { const hostName = this.randomHostname(); + const isIsolated = this.randomBoolean(); + return { agent: { version: this.randomVersion(), @@ -465,10 +467,10 @@ export class EndpointDocGenerator extends BaseDataGenerator { applied: this.randomChoice(APPLIED_POLICIES), }, configuration: { - isolation: false, + isolation: isIsolated, }, state: { - isolation: false, + isolation: isIsolated, }, }, }; diff --git a/x-pack/plugins/security_solution/common/endpoint/index_data.ts b/x-pack/plugins/security_solution/common/endpoint/index_data.ts index 0dc7891560c2d..021b9bcb1eccc 100644 --- a/x-pack/plugins/security_solution/common/endpoint/index_data.ts +++ b/x-pack/plugins/security_solution/common/endpoint/index_data.ts @@ -28,8 +28,10 @@ import { policyFactory as policyConfigFactory } from './models/policy_config'; import { HostMetadata } from './types'; import { KbnClientWithApiKeySupport } from '../../scripts/endpoint/kbn_client_with_api_key_support'; import { FleetAgentGenerator } from './data_generators/fleet_agent_generator'; +import { FleetActionGenerator } from './data_generators/fleet_action_generator'; const fleetAgentGenerator = new FleetAgentGenerator(); +const fleetActionGenerator = new FleetActionGenerator(); export async function indexHostsAndAlerts( client: Client, @@ -175,6 +177,9 @@ async function indexHostDocs({ }, }, }; + + // Create some actions for this Host + await indexFleetActionsForHost(client, hostMetadata); } await client.index({ @@ -397,3 +402,43 @@ const indexFleetAgentForHost = async ( return agentDoc; }; + +const indexFleetActionsForHost = async ( + esClient: Client, + endpointHost: HostMetadata +): Promise => { + const ES_INDEX_OPTIONS = { headers: { 'X-elastic-product-origin': 'fleet' } }; + const agentId = endpointHost.elastic.agent.id; + + for (let i = 0; i < 5; i++) { + // create an action + const isolateAction = fleetActionGenerator.generate({ + data: { comment: 'data generator: this host is bad' }, + }); + + isolateAction.agents = [agentId]; + + await esClient.index( + { + index: '.fleet-actions', + body: isolateAction, + }, + ES_INDEX_OPTIONS + ); + + // Create an action response for the above + const unIsolateAction = fleetActionGenerator.generateResponse({ + action_id: isolateAction.action_id, + agent_id: agentId, + action_data: isolateAction.data, + }); + + await esClient.index( + { + index: '.fleet-actions-results', + body: unIsolateAction, + }, + ES_INDEX_OPTIONS + ); + } +}; diff --git a/x-pack/plugins/security_solution/common/endpoint/types/actions.ts b/x-pack/plugins/security_solution/common/endpoint/types/actions.ts index 99dac5ea5cda6..fcfda9c9a30d9 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types/actions.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types/actions.ts @@ -24,6 +24,21 @@ export interface EndpointAction { }; } +export interface EndpointActionResponse { + '@timestamp': string; + /** The id of the action for which this response is associated with */ + action_id: string; + /** The agent id that sent this action response */ + agent_id: string; + started_at: string; + completed_at: string; + error: string; + action_data: { + command: ISOLATION_ACTIONS; + comment?: string; + }; +} + export type HostIsolationRequestBody = TypeOf; export interface HostIsolationResponse { diff --git a/x-pack/plugins/security_solution/public/resolver/models/indexed_process_tree/__snapshots__/isometric_taxi_layout.test.ts.snap b/x-pack/plugins/security_solution/public/resolver/models/indexed_process_tree/__snapshots__/isometric_taxi_layout.test.ts.snap index 7a79adbff2d74..1eda3a0980191 100644 --- a/x-pack/plugins/security_solution/public/resolver/models/indexed_process_tree/__snapshots__/isometric_taxi_layout.test.ts.snap +++ b/x-pack/plugins/security_solution/public/resolver/models/indexed_process_tree/__snapshots__/isometric_taxi_layout.test.ts.snap @@ -15,11 +15,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "A", - "process.name": "powershell.exe", + "process.name": "lsass.exe", "process.parent.entity_id": "", }, "id": "A", - "name": "powershell.exe", + "name": "lsass.exe", "parent": undefined, "stats": Object { "byCategory": Object {}, @@ -33,11 +33,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "A", - "process.name": "powershell.exe", + "process.name": "lsass.exe", "process.parent.entity_id": "", }, "id": "A", - "name": "powershell.exe", + "name": "lsass.exe", "parent": undefined, "stats": Object { "byCategory": Object {}, @@ -58,11 +58,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "A", - "process.name": "lsass.exe", + "process.name": "notepad.exe", "process.parent.entity_id": "", }, "id": "A", - "name": "lsass.exe", + "name": "notepad.exe", "parent": undefined, "stats": Object { "byCategory": Object {}, @@ -73,11 +73,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "B", - "process.name": "lsass.exe", + "process.name": "mimikatz.exe", "process.parent.entity_id": "A", }, "id": "B", - "name": "lsass.exe", + "name": "mimikatz.exe", "parent": "A", "stats": Object { "byCategory": Object {}, @@ -88,11 +88,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "C", - "process.name": "powershell.exe", + "process.name": "notepad.exe", "process.parent.entity_id": "A", }, "id": "C", - "name": "powershell.exe", + "name": "notepad.exe", "parent": "A", "stats": Object { "byCategory": Object {}, @@ -103,11 +103,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "I", - "process.name": "lsass.exe", + "process.name": "mimikatz.exe", "process.parent.entity_id": "A", }, "id": "I", - "name": "lsass.exe", + "name": "mimikatz.exe", "parent": "A", "stats": Object { "byCategory": Object {}, @@ -118,11 +118,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "D", - "process.name": "notepad.exe", + "process.name": "powershell.exe", "process.parent.entity_id": "B", }, "id": "D", - "name": "notepad.exe", + "name": "powershell.exe", "parent": "B", "stats": Object { "byCategory": Object {}, @@ -133,11 +133,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "E", - "process.name": "notepad.exe", + "process.name": "powershell.exe", "process.parent.entity_id": "B", }, "id": "E", - "name": "notepad.exe", + "name": "powershell.exe", "parent": "B", "stats": Object { "byCategory": Object {}, @@ -148,11 +148,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "F", - "process.name": "explorer.exe", + "process.name": "iexlorer.exe", "process.parent.entity_id": "C", }, "id": "F", - "name": "explorer.exe", + "name": "iexlorer.exe", "parent": "C", "stats": Object { "byCategory": Object {}, @@ -163,11 +163,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "G", - "process.name": "explorer.exe", + "process.name": "iexlorer.exe", "process.parent.entity_id": "C", }, "id": "G", - "name": "explorer.exe", + "name": "iexlorer.exe", "parent": "C", "stats": Object { "byCategory": Object {}, @@ -178,11 +178,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "H", - "process.name": "mimikatz.exe", + "process.name": "iexlorer.exe", "process.parent.entity_id": "G", }, "id": "H", - "name": "mimikatz.exe", + "name": "iexlorer.exe", "parent": "G", "stats": Object { "byCategory": Object {}, @@ -439,11 +439,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "A", - "process.name": "lsass.exe", + "process.name": "notepad.exe", "process.parent.entity_id": "", }, "id": "A", - "name": "lsass.exe", + "name": "notepad.exe", "parent": undefined, "stats": Object { "byCategory": Object {}, @@ -457,11 +457,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "B", - "process.name": "lsass.exe", + "process.name": "mimikatz.exe", "process.parent.entity_id": "A", }, "id": "B", - "name": "lsass.exe", + "name": "mimikatz.exe", "parent": "A", "stats": Object { "byCategory": Object {}, @@ -475,11 +475,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "C", - "process.name": "powershell.exe", + "process.name": "notepad.exe", "process.parent.entity_id": "A", }, "id": "C", - "name": "powershell.exe", + "name": "notepad.exe", "parent": "A", "stats": Object { "byCategory": Object {}, @@ -493,11 +493,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "I", - "process.name": "lsass.exe", + "process.name": "mimikatz.exe", "process.parent.entity_id": "A", }, "id": "I", - "name": "lsass.exe", + "name": "mimikatz.exe", "parent": "A", "stats": Object { "byCategory": Object {}, @@ -511,11 +511,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "D", - "process.name": "notepad.exe", + "process.name": "powershell.exe", "process.parent.entity_id": "B", }, "id": "D", - "name": "notepad.exe", + "name": "powershell.exe", "parent": "B", "stats": Object { "byCategory": Object {}, @@ -529,11 +529,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "E", - "process.name": "notepad.exe", + "process.name": "powershell.exe", "process.parent.entity_id": "B", }, "id": "E", - "name": "notepad.exe", + "name": "powershell.exe", "parent": "B", "stats": Object { "byCategory": Object {}, @@ -547,11 +547,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "F", - "process.name": "explorer.exe", + "process.name": "iexlorer.exe", "process.parent.entity_id": "C", }, "id": "F", - "name": "explorer.exe", + "name": "iexlorer.exe", "parent": "C", "stats": Object { "byCategory": Object {}, @@ -565,11 +565,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "G", - "process.name": "explorer.exe", + "process.name": "iexlorer.exe", "process.parent.entity_id": "C", }, "id": "G", - "name": "explorer.exe", + "name": "iexlorer.exe", "parent": "C", "stats": Object { "byCategory": Object {}, @@ -583,11 +583,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "H", - "process.name": "mimikatz.exe", + "process.name": "iexlorer.exe", "process.parent.entity_id": "G", }, "id": "H", - "name": "mimikatz.exe", + "name": "iexlorer.exe", "parent": "G", "stats": Object { "byCategory": Object {}, @@ -608,11 +608,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "A", - "process.name": "iexlorer.exe", + "process.name": "powershell.exe", "process.parent.entity_id": "", }, "id": "A", - "name": "iexlorer.exe", + "name": "powershell.exe", "parent": undefined, "stats": Object { "byCategory": Object {}, @@ -623,11 +623,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "B", - "process.name": "notepad.exe", + "process.name": "powershell.exe", "process.parent.entity_id": "A", }, "id": "B", - "name": "notepad.exe", + "name": "powershell.exe", "parent": "A", "stats": Object { "byCategory": Object {}, @@ -661,11 +661,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "A", - "process.name": "iexlorer.exe", + "process.name": "powershell.exe", "process.parent.entity_id": "", }, "id": "A", - "name": "iexlorer.exe", + "name": "powershell.exe", "parent": undefined, "stats": Object { "byCategory": Object {}, @@ -679,11 +679,11 @@ Object { "data": Object { "@timestamp": 1606234833273, "process.entity_id": "B", - "process.name": "notepad.exe", + "process.name": "powershell.exe", "process.parent.entity_id": "A", }, "id": "B", - "name": "notepad.exe", + "name": "powershell.exe", "parent": "A", "stats": Object { "byCategory": Object {},