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..d1b107b5396dd 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 @@ -33,6 +35,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 Math.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.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 {