diff --git a/x-pack/plugins/endpoint/common/generate_data.test.ts b/x-pack/plugins/endpoint/common/generate_data.test.ts new file mode 100644 index 000000000000..ebe3c25eef82 --- /dev/null +++ b/x-pack/plugins/endpoint/common/generate_data.test.ts @@ -0,0 +1,168 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { EndpointDocGenerator, Event } from './generate_data'; + +interface Node { + events: Event[]; + children: Node[]; + parent_entity_id?: string; +} + +describe('data generator', () => { + let generator: EndpointDocGenerator; + beforeEach(() => { + generator = new EndpointDocGenerator('seed'); + }); + + it('creates the same documents with same random seed', () => { + const generator1 = new EndpointDocGenerator('seed'); + const generator2 = new EndpointDocGenerator('seed'); + const timestamp = new Date().getTime(); + const metadata1 = generator1.generateEndpointMetadata(timestamp); + const metadata2 = generator2.generateEndpointMetadata(timestamp); + expect(metadata1).toEqual(metadata2); + }); + + it('creates different documents with different random seeds', () => { + const generator1 = new EndpointDocGenerator('seed'); + const generator2 = new EndpointDocGenerator('different seed'); + const timestamp = new Date().getTime(); + const metadata1 = generator1.generateEndpointMetadata(timestamp); + const metadata2 = generator2.generateEndpointMetadata(timestamp); + expect(metadata1).not.toEqual(metadata2); + }); + + it('creates endpoint metadata documents', () => { + const timestamp = new Date().getTime(); + const metadata = generator.generateEndpointMetadata(timestamp); + expect(metadata['@timestamp']).toEqual(timestamp); + expect(metadata.event.created).toEqual(timestamp); + expect(metadata.endpoint).not.toBeNull(); + expect(metadata.agent).not.toBeNull(); + expect(metadata.host).not.toBeNull(); + }); + + it('creates alert event documents', () => { + const timestamp = new Date().getTime(); + const alert = generator.generateAlert(timestamp); + expect(alert['@timestamp']).toEqual(timestamp); + expect(alert.event.action).not.toBeNull(); + expect(alert.endpoint).not.toBeNull(); + expect(alert.agent).not.toBeNull(); + expect(alert.host).not.toBeNull(); + expect(alert.process.entity_id).not.toBeNull(); + }); + + it('creates process event documents', () => { + const timestamp = new Date().getTime(); + const processEvent = generator.generateEvent({ timestamp }); + expect(processEvent['@timestamp']).toEqual(timestamp); + expect(processEvent.event.category).toEqual('process'); + expect(processEvent.event.kind).toEqual('event'); + expect(processEvent.event.type).toEqual('creation'); + expect(processEvent.agent).not.toBeNull(); + expect(processEvent.host).not.toBeNull(); + expect(processEvent.process.entity_id).not.toBeNull(); + }); + + it('creates other event documents', () => { + const timestamp = new Date().getTime(); + const processEvent = generator.generateEvent({ timestamp, eventCategory: 'dns' }); + expect(processEvent['@timestamp']).toEqual(timestamp); + expect(processEvent.event.category).toEqual('dns'); + expect(processEvent.event.kind).toEqual('event'); + expect(processEvent.event.type).toEqual('creation'); + expect(processEvent.agent).not.toBeNull(); + expect(processEvent.host).not.toBeNull(); + expect(processEvent.process.entity_id).not.toBeNull(); + }); + + describe('creates alert ancestor tree', () => { + let events: Event[]; + + beforeEach(() => { + events = generator.generateAlertEventAncestry(3); + }); + + it('with n-1 process events', () => { + for (let i = 1; i < events.length - 1; i++) { + expect(events[i].process.parent?.entity_id).toEqual(events[i - 1].process.entity_id); + expect(events[i].event.kind).toEqual('event'); + expect(events[i].event.category).toEqual('process'); + } + }); + + it('with a corresponding alert at the end', () => { + // The alert should be last and have the same entity_id as the previous process event + expect(events[events.length - 1].process.entity_id).toEqual( + events[events.length - 2].process.entity_id + ); + expect(events[events.length - 1].process.parent?.entity_id).toEqual( + events[events.length - 2].process.parent?.entity_id + ); + expect(events[events.length - 1].event.kind).toEqual('alert'); + expect(events[events.length - 1].event.category).toEqual('malware'); + }); + }); + + function buildResolverTree(events: Event[]): Node { + // First pass we gather up all the events by entity_id + const tree: Record = {}; + events.forEach(event => { + if (event.process.entity_id in tree) { + tree[event.process.entity_id].events.push(event); + } else { + tree[event.process.entity_id] = { + events: [event], + children: [], + parent_entity_id: event.process.parent?.entity_id, + }; + } + }); + // Second pass add child references to each node + for (const value of Object.values(tree)) { + if (value.parent_entity_id) { + tree[value.parent_entity_id].children.push(value); + } + } + // The root node must be first in the array or this fails + return tree[events[0].process.entity_id]; + } + + function countResolverEvents(rootNode: Node, generations: number): number { + // Start at the root, traverse N levels of the tree and check that we found all nodes + let nodes = [rootNode]; + let visitedEvents = 0; + for (let i = 0; i < generations + 1; i++) { + let nextNodes: Node[] = []; + nodes.forEach(node => { + nextNodes = nextNodes.concat(node.children); + visitedEvents += node.events.length; + }); + nodes = nextNodes; + } + return visitedEvents; + } + + it('creates tree of process children', () => { + const timestamp = new Date().getTime(); + const root = generator.generateEvent({ timestamp }); + const generations = 2; + const events = generator.generateDescendantsTree(root, generations); + const rootNode = buildResolverTree(events); + const visitedEvents = countResolverEvents(rootNode, generations); + expect(visitedEvents).toEqual(events.length); + }); + + it('creates full resolver tree', () => { + const alertAncestors = 3; + const generations = 2; + const events = generator.generateFullResolverTree(alertAncestors, generations); + const rootNode = buildResolverTree(events); + const visitedEvents = countResolverEvents(rootNode, alertAncestors + generations); + expect(visitedEvents).toEqual(events.length); + }); +}); diff --git a/x-pack/plugins/endpoint/common/generate_data.ts b/x-pack/plugins/endpoint/common/generate_data.ts new file mode 100644 index 000000000000..a91cf0ffca78 --- /dev/null +++ b/x-pack/plugins/endpoint/common/generate_data.ts @@ -0,0 +1,431 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import uuid from 'uuid'; +import seedrandom from 'seedrandom'; +import { AlertEvent, EndpointEvent, EndpointMetadata, OSFields } from './types'; + +export type Event = AlertEvent | EndpointEvent; + +interface EventOptions { + timestamp?: number; + entityID?: string; + parentEntityID?: string; + eventType?: string; + eventCategory?: string; +} + +const Windows: OSFields[] = [ + { + name: 'windows 10.0', + full: 'Windows 10', + version: '10.0', + variant: 'Windows Pro', + }, + { + name: 'windows 10.0', + full: 'Windows Server 2016', + version: '10.0', + variant: 'Windows Server', + }, + { + name: 'windows 6.2', + full: 'Windows Server 2012', + version: '6.2', + variant: 'Windows Server', + }, + { + name: 'windows 6.3', + full: 'Windows Server 2012R2', + version: '6.3', + variant: 'Windows Server Release 2', + }, +]; + +const Linux: OSFields[] = []; + +const Mac: OSFields[] = []; + +const OS: OSFields[] = [...Windows, ...Mac, ...Linux]; + +const POLICIES: Array<{ name: string; id: string }> = [ + { + name: 'Default', + id: '00000000-0000-0000-0000-000000000000', + }, + { + name: 'With Eventing', + id: 'C2A9093E-E289-4C0A-AA44-8C32A414FA7A', + }, +]; + +const FILE_OPERATIONS: string[] = ['creation', 'open', 'rename', 'execution', 'deletion']; + +// These are from the v1 schemas and aren't all valid ECS event categories, still in flux +const OTHER_EVENT_CATEGORIES: string[] = ['driver', 'file', 'library', 'network', 'registry']; + +export class EndpointDocGenerator { + agentId: string; + hostId: string; + hostname: string; + macAddress: string[]; + ip: string[]; + agentVersion: string; + os: OSFields; + policy: { name: string; id: string }; + random: seedrandom.prng; + + constructor(seed = Math.random().toString()) { + this.random = seedrandom(seed); + this.hostId = this.seededUUIDv4(); + this.agentId = this.seededUUIDv4(); + this.hostname = this.randomHostname(); + this.ip = this.randomArray(3, () => this.randomIP()); + this.macAddress = this.randomArray(3, () => this.randomMac()); + this.agentVersion = this.randomVersion(); + this.os = this.randomChoice(OS); + this.policy = this.randomChoice(POLICIES); + } + + public randomizeIPs() { + this.ip = this.randomArray(3, () => this.randomIP()); + } + + public generateEndpointMetadata(ts = new Date().getTime()): EndpointMetadata { + return { + '@timestamp': ts, + event: { + created: ts, + }, + endpoint: { + policy: { + id: this.policy.id, + }, + }, + agent: { + version: this.agentVersion, + id: this.agentId, + }, + host: { + id: this.hostId, + hostname: this.hostname, + ip: this.ip, + mac: this.macAddress, + os: this.os, + }, + }; + } + + public generateAlert( + ts = new Date().getTime(), + entityID = this.randomString(10), + parentEntityID?: string + ): AlertEvent { + return { + '@timestamp': ts, + agent: { + id: this.agentId, + version: this.agentVersion, + }, + event: { + action: this.randomChoice(FILE_OPERATIONS), + kind: 'alert', + category: 'malware', + id: this.seededUUIDv4(), + dataset: 'endpoint', + module: 'endpoint', + type: 'creation', + }, + endpoint: { + policy: { + id: this.policy.id, + }, + }, + file: { + owner: 'SYSTEM', + name: 'fake_malware.exe', + path: 'C:/fake_malware.exe', + accessed: ts, + mtime: ts, + created: ts, + size: 3456, + hash: { + md5: 'fake file md5', + sha1: 'fake file sha1', + sha256: 'fake file sha256', + }, + code_signature: { + trusted: false, + subject_name: 'bad signer', + }, + malware_classifier: { + identifier: 'endpointpe', + score: 1, + threshold: 0.66, + version: '3.0.33', + }, + temp_file_path: 'C:/temp/fake_malware.exe', + }, + host: { + id: this.hostId, + hostname: this.hostname, + ip: this.ip, + mac: this.macAddress, + os: this.os, + }, + process: { + pid: 2, + name: 'malware writer', + start: ts, + uptime: 0, + user: 'SYSTEM', + entity_id: entityID, + executable: 'C:/malware.exe', + parent: parentEntityID ? { entity_id: parentEntityID, pid: 1 } : undefined, + token: { + domain: 'NT AUTHORITY', + integrity_level: 16384, + integrity_level_name: 'system', + privileges: [ + { + description: 'Replace a process level token', + enabled: false, + name: 'SeAssignPrimaryTokenPrivilege', + }, + ], + sid: 'S-1-5-18', + type: 'tokenPrimary', + user: 'SYSTEM', + }, + code_signature: { + trusted: false, + subject_name: 'bad signer', + }, + hash: { + md5: 'fake md5', + sha1: 'fake sha1', + sha256: 'fake sha256', + }, + }, + dll: [ + { + pe: { + architecture: 'x64', + imphash: 'c30d230b81c734e82e86e2e2fe01cd01', + }, + code_signature: { + subject_name: 'Cybereason Inc', + trusted: true, + }, + compile_time: 1534424710, + hash: { + md5: '1f2d082566b0fc5f2c238a5180db7451', + sha1: 'ca85243c0af6a6471bdaa560685c51eefd6dbc0d', + sha256: '8ad40c90a611d36eb8f9eb24fa04f7dbca713db383ff55a03aa0f382e92061a2', + }, + malware_classifier: { + identifier: 'Whitelisted', + score: 0, + threshold: 0, + version: '3.0.0', + }, + mapped_address: 5362483200, + mapped_size: 0, + path: 'C:\\Program Files\\Cybereason ActiveProbe\\AmSvc.exe', + }, + ], + }; + } + + public generateEvent(options: EventOptions = {}): EndpointEvent { + return { + '@timestamp': options.timestamp ? options.timestamp : new Date().getTime(), + agent: { + id: this.agentId, + version: this.agentVersion, + type: 'endpoint', + }, + ecs: { + version: '1.4.0', + }, + event: { + category: options.eventCategory ? options.eventCategory : 'process', + kind: 'event', + type: options.eventType ? options.eventType : 'creation', + id: this.seededUUIDv4(), + }, + host: { + id: this.hostId, + hostname: this.hostname, + ip: this.ip, + mac: this.macAddress, + os: this.os, + }, + process: { + entity_id: options.entityID ? options.entityID : this.randomString(10), + parent: options.parentEntityID ? { entity_id: options.parentEntityID } : undefined, + }, + }; + } + + public generateFullResolverTree( + alertAncestors?: number, + childGenerations?: number, + maxChildrenPerNode?: number, + relatedEventsPerNode?: number, + percentNodesWithRelated?: number, + percentChildrenTerminated?: number + ): Event[] { + const ancestry = this.generateAlertEventAncestry(alertAncestors); + // ancestry will always have at least 2 elements, and the second to last element will be the process associated with the alert + const descendants = this.generateDescendantsTree( + ancestry[ancestry.length - 2], + childGenerations, + maxChildrenPerNode, + relatedEventsPerNode, + percentNodesWithRelated, + percentChildrenTerminated + ); + return ancestry.concat(descendants); + } + + public generateAlertEventAncestry(alertAncestors = 3): Event[] { + const events = []; + const startDate = new Date().getTime(); + const root = this.generateEvent({ timestamp: startDate + 1000 }); + events.push(root); + let ancestor = root; + for (let i = 0; i < alertAncestors; i++) { + ancestor = this.generateEvent({ + timestamp: startDate + 1000 * (i + 1), + parentEntityID: ancestor.process.entity_id, + }); + events.push(ancestor); + } + events.push( + this.generateAlert( + startDate + 1000 * alertAncestors, + ancestor.process.entity_id, + ancestor.process.parent?.entity_id + ) + ); + return events; + } + + public generateDescendantsTree( + root: Event, + generations = 2, + maxChildrenPerNode = 2, + relatedEventsPerNode = 3, + percentNodesWithRelated = 100, + percentChildrenTerminated = 100 + ): Event[] { + let events: Event[] = [root]; + let parents = [root]; + let timestamp = root['@timestamp']; + for (let i = 0; i < generations; i++) { + const newParents: EndpointEvent[] = []; + parents.forEach(element => { + // const numChildren = randomN(maxChildrenPerNode); + const numChildren = maxChildrenPerNode; + for (let j = 0; j < numChildren; j++) { + timestamp = timestamp + 1000; + const child = this.generateEvent({ + timestamp, + parentEntityID: element.process.entity_id, + }); + newParents.push(child); + } + }); + events = events.concat(newParents); + parents = newParents; + } + const terminationEvents: EndpointEvent[] = []; + let relatedEvents: EndpointEvent[] = []; + events.forEach(element => { + if (this.randomN(100) < percentChildrenTerminated) { + timestamp = timestamp + 1000; + terminationEvents.push( + this.generateEvent({ + timestamp, + entityID: element.process.entity_id, + parentEntityID: element.process.parent?.entity_id, + eventCategory: 'process', + eventType: 'end', + }) + ); + } + if (this.randomN(100) < percentNodesWithRelated) { + relatedEvents = relatedEvents.concat( + this.generateRelatedEvents(element, relatedEventsPerNode) + ); + } + }); + events = events.concat(terminationEvents); + events = events.concat(relatedEvents); + return events; + } + + public generateRelatedEvents(node: Event, numRelatedEvents = 10): EndpointEvent[] { + const ts = node['@timestamp'] + 1000; + const relatedEvents: EndpointEvent[] = []; + for (let i = 0; i < numRelatedEvents; i++) { + relatedEvents.push( + this.generateEvent({ + timestamp: ts, + entityID: node.process.entity_id, + parentEntityID: node.process.parent?.entity_id, + eventCategory: this.randomChoice(OTHER_EVENT_CATEGORIES), + }) + ); + } + return relatedEvents; + } + + private randomN(n: number): number { + return Math.floor(this.random() * n); + } + + private *randomNGenerator(max: number, count: number) { + while (count > 0) { + yield this.randomN(max); + count--; + } + } + + private randomArray(lengthLimit: number, generator: () => T): T[] { + const rand = this.randomN(lengthLimit) + 1; + return [...Array(rand).keys()].map(generator); + } + + private randomMac(): string { + return [...this.randomNGenerator(255, 6)].map(x => x.toString(16)).join('-'); + } + + private randomIP(): string { + return [10, ...this.randomNGenerator(255, 3)].map(x => x.toString()).join('.'); + } + + private randomVersion(): string { + return [6, ...this.randomNGenerator(10, 2)].map(x => x.toString()).join('.'); + } + + private randomChoice(choices: T[]): T { + return choices[this.randomN(choices.length)]; + } + + private randomString(length: number): string { + return [...this.randomNGenerator(36, length)].map(x => x.toString(36)).join(''); + } + + private randomHostname(): string { + return `Host-${this.randomString(10)}`; + } + + private seededUUIDv4(): string { + return uuid.v4({ random: [...this.randomNGenerator(255, 16)] }); + } +} diff --git a/x-pack/plugins/endpoint/common/types.ts b/x-pack/plugins/endpoint/common/types.ts index edcd2d7841b1..1c438c40fa38 100644 --- a/x-pack/plugins/endpoint/common/types.ts +++ b/x-pack/plugins/endpoint/common/types.ts @@ -167,29 +167,34 @@ export type AlertEvent = Immutable<{ module: string; type: string; }; + endpoint: { + policy: { + id: string; + }; + }; process: { code_signature: { subject_name: string; trusted: boolean; }; - command_line: string; - domain: string; + command_line?: string; + domain?: string; pid: number; - ppid: number; + ppid?: number; entity_id: string; - parent: { + parent?: { pid: number; entity_id: string; }; name: string; hash: HashFields; - pe: { + pe?: { imphash: string; }; executable: string; - sid: string; + sid?: string; start: number; - malware_classifier: MalwareClassifierFields; + malware_classifier?: MalwareClassifierFields; token: { domain: string; type: string; @@ -197,9 +202,9 @@ export type AlertEvent = Immutable<{ sid: string; integrity_level: number; integrity_level_name: string; - privileges: PrivilegesFields[]; + privileges?: PrivilegesFields[]; }; - thread: ThreadFields[]; + thread?: ThreadFields[]; uptime: number; user: string; }; @@ -212,32 +217,20 @@ export type AlertEvent = Immutable<{ created: number; size: number; hash: HashFields; - pe: { + pe?: { imphash: string; }; code_signature: { trusted: boolean; subject_name: string; }; - malware_classifier: { - features: { - data: { - buffer: string; - decompressed_size: number; - encoding: string; - }; - }; - } & MalwareClassifierFields; + malware_classifier: MalwareClassifierFields; temp_file_path: string; }; host: HostFields; - thread: {}; - dll: DllFields[]; + dll?: DllFields[]; }>; -/** - * Metadata associated with an alert event. - */ interface AlertMetadata { id: string; @@ -252,9 +245,9 @@ interface AlertMetadata { export type AlertData = AlertEvent & AlertMetadata; export interface EndpointMetadata { - '@timestamp': string; + '@timestamp': number; event: { - created: Date; + created: number; }; endpoint: { policy: { @@ -262,8 +255,8 @@ export interface EndpointMetadata { }; }; agent: { - version: string; id: string; + version: string; }; host: HostFields; } @@ -310,22 +303,32 @@ export interface LegacyEndpointEvent { export interface EndpointEvent { '@timestamp': number; + agent: { + id: string; + version: string; + type: string; + }; + ecs: { + version: string; + }; event: { category: string; type: string; id: string; + kind: string; }; - endpoint: { - process: { - entity_id: string; - parent: { - entity_id: string; - }; - }; - }; - agent: { + host: { id: string; - type: string; + hostname: string; + ip: string[]; + mac: string[]; + os: OSFields; + }; + process: { + entity_id: string; + parent?: { + entity_id: string; + }; }; } diff --git a/x-pack/plugins/endpoint/package.json b/x-pack/plugins/endpoint/package.json index 25afe2c8442b..c7ba8b3fb419 100644 --- a/x-pack/plugins/endpoint/package.json +++ b/x-pack/plugins/endpoint/package.json @@ -6,9 +6,11 @@ "license": "Elastic-License", "scripts": {}, "dependencies": { - "react-redux": "^7.1.0" + "react-redux": "^7.1.0", + "seedrandom": "^3.0.5" }, "devDependencies": { + "@types/seedrandom": ">=2.0.0 <4.0.0", "@types/react-redux": "^7.1.0", "redux-devtools-extension": "^2.13.8" } diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/mock_alert_result_list.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/mock_alert_result_list.ts index 7db94fc9d426..3931723a5550 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/mock_alert_result_list.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/mock_alert_result_list.ts @@ -5,6 +5,7 @@ */ import { AlertResultList } from '../../../../../common/types'; +import { EndpointDocGenerator } from '../../../../../common/generate_data'; export const mockAlertResultList: (options?: { total?: number; @@ -24,160 +25,15 @@ export const mockAlertResultList: (options?: { const actualCountToReturn = Math.max(Math.min(total - numberToSkip, requestPageSize), 0); const alerts = []; + const generator = new EndpointDocGenerator(); for (let index = 0; index < actualCountToReturn; index++) { alerts.push({ - '@timestamp': 1542341895000, - id: 'xDUYMHABAJk0XnHd8rrd', - agent: { - id: 'ced9c68e-b94a-4d66-bb4c-6106514f0a2f', - version: '3.0.0', + ...generator.generateAlert(new Date().getTime() + index * 1000), + ...{ + id: 'xDUYMHABAJk0XnHd8rrd' + index, + prev: null, + next: null, }, - host: { - id: 'xrctvybuni', - hostname: 'HD-c15-bc09190a', - ip: ['10.179.244.14'], - mac: ['xsertcyvbunimkn56edtyf'], - os: { - full: 'Windows 10', - name: 'windows', - version: '10', - variant: '3', - }, - }, - thread: {}, - prev: null, - next: null, - event: { - id: '2f1c0928-3876-4e11-acbb-9199257c7b1c', - action: 'creation', - category: 'malware', - dataset: 'endpoint', - kind: 'alert', - module: 'endpoint', - type: 'creation', - }, - file: { - accessed: 1542789400, - created: 1542789400, - hash: { - md5: '4ace3baaa509d08510405e1b169e325b', - sha1: '27fb21cf5db95ffca43b234affa99becc4023b9d', - sha256: '6ed1c836dbf099be7845bdab7671def2c157643761b52251e04e9b6ee109ec75', - }, - pe: { - imphash: '835d619dfdf3cc727cebd91300ab3462', - }, - mtime: 1542789400, - owner: 'Administrators', - name: 'test name', - path: 'C:\\Windows\\TEMP\\tmp0000008f\\tmp00001be5', - size: 188416, - code_signature: { - subject_name: 'Cybereason Inc', - trusted: false, - }, - malware_classifier: { - features: { - data: { - buffer: - 'eAHtnU1oHHUUwHsQ7MGDiIIUD4sH8WBBxJtopiLoUY0pYo2ZTbJJ0yQ17m4+ms/NRzeVWpuUWCL4sWlEYvFQ8KJQ6NCTEA8eRD30sIo3PdSriLi7837Pko3LbHZ2M5m+XObHm/d/X////83O7jCZvzacHBpPplNdfalkdjSdyty674Ft59dN71Dpb9v5eKh8LMEHjsCF2wIfVlRKsHROYPGkQO5+gY2vBSYYdWZFYGwEO/cITHMqkxPYnBBY+07gtCuQ9gSGigJ5lPPYGXcE+jA4z3Ad1ZtAUiDUyrEEPYzqRnIKgxd/Rgc7gygPo5wn95PouN7OeEYJ1UXiJgRmvscgp/LOziIkkSyT+xRVnXhZ4DKh5goCkzidRHkGO4uvCyw9LDDtCay8ILCAzrJOJaGuZwUuvSewivJVIPsklq8JbL4qMJsTSCcExrGs83WKU295ZFo5lr2TaZbcUw5FeJy8tgTeLpCy2iGeS67ABXzlgbEi1UC5FxcZnA4y/CLK82Qxi847FGGZRTLsCUxR1aWEwOp1AmOjDRYYzgwusL9WfqBiGJxnVAanixTq7Dp22LBdlWMJzlOx8wmBK2Rx5WmBLJIRwtAijOQE+ooCb2B5xBOYRtlfNeXpLpA7oyZRTqHzGenkmIJPnhBIMrzTwSA6H93CO5l+c1NA99f6IwLH8fUKdjTmDpTbgS50+gGVnECnE4PpooC2guPoaPADSHrcncNHmEHtAFkq3+EI+A37zsrrTvH3WTkvJLoOTyBp10wx2JcgVCRahA4NrICE4a+hrMXsA3qAHItW188E8ejO7XV3eh/KCYwxlamEwCgL8lN2wTntfrhY/U0g/5KAdvUpT+AszWqBdqH7VLeeZrExK9Cv1UgIDKA8g/cx7QAEP+AhAfRaMKB2HOJh+BSFSqKjSytNGBlc6PrpxvK7lCVDxbSG3Z7AhCMwx6gelwgLAltXBXJUTH29j+U1LHdipx/QprfKfGnF0sBpdBYxmEQyTzW0h6/0khcuhhJYRufym+i4VKMocJMs/KvfoW3/UJb4PeZOSZVONThZz4djP/75TAXa/CVfOvX3RgVLIDreLPN1pP1osW7lGmHsEhjBOzf+EPBE4vndvWz5xb/cChxGcv1LAb+tluALKnZ47isf1MXvz1ZMlsCXbXtPceqhrcp1ps6YHwQeBXLEPCf7q23tl9uJui0bGBgYRAccv7uXr/g5Af+2oNTrpgTa/vnpjBvpLAwM4gRBPvIZGBgYGBgYGBgYGBgYGBgYGBgYGBgYNAOc9oMXs4GBgYFBcNBnww5QzDXgRtPSaZ5lg/itsRaslgZ3bnWEEVnhMetIBwiiVnlbCbWrEftrt11zdwWnseFW1QO63w1is3ptD1pV9xG0t+zvfUrzrvh380qwXWAVCw6h78GIfG7ZlzltXu6hd+y92fECRFhjuH3bXG8N43oXEHperdzvUbteaDxhVTUeq25fqhG1X6Ai8mtF6BDXz2wR+dzSgg4Qsxls5T11XMG+82y8GkG+b7kL69xg7mF1SFvhBgYGsYH/Xi7HE+PVkiB2jt1bNZxT+k4558jR53ydz5//1m1KOgYGBgYGBgYGEQfnsYaG2z1sdPJS79XQSu91ndobOAHCaN5vNzUk1bceQVzUpbw3iOuT+UFmR18bHrp3gyhDC56lCd1y85w2+HSNUwVhhdGC7blLf+bV/fqtvhMg1NDjCcugB1QXswbs8ekj/v1BgzFHBIIsyP+HfwFdMpzu', - decompressed_size: 27831, - encoding: 'zlib', - }, - }, - identifier: 'endpointpe', - score: 1, - threshold: 0.66, - version: '3.0.33', - }, - temp_file_path: 'C:\\Windows\\TEMP\\1bb9abfc-ca14-47b2-9f2c-10c323df42f9', - }, - process: { - pid: 1076, - ppid: 432, - entity_id: 'wertqwer', - parent: { - pid: 432, - entity_id: 'adsfsdaf', - }, - name: 'test name', - code_signature: { - subject_name: 'Cybereason Inc', - trusted: true, - }, - command_line: '"C:\\Program Files\\Cybereason ActiveProbe\\AmSvc.exe"', - domain: 'NT AUTHORITY', - executable: 'C:\\Program Files\\Cybereason ActiveProbe\\AmSvc.exe', - hash: { - md5: '1f2d082566b0fc5f2c238a5180db7451', - sha1: 'ca85243c0af6a6471bdaa560685c51eefd6dbc0d', - sha256: '8ad40c90a611d36eb8f9eb24fa04f7dbca713db383ff55a03aa0f382e92061a2', - }, - pe: { - imphash: 'c30d230b81c734e82e86e2e2fe01cd01', - }, - malware_classifier: { - identifier: 'Whitelisted', - score: 0, - threshold: 0, - version: '3.0.0', - }, - thread: [ - { - id: 1652, - service_name: 'CybereasonAntiMalware', - start: 1542788400, - start_address: 8791698721056, - start_address_module: 'C:\\Program Files\\Cybereason ActiveProbe\\gzfltum.dll', - }, - ], - sid: 'S-1-5-18', - start: 1542788400, - token: { - domain: 'NT AUTHORITY', - integrity_level: 16384, - integrity_level_name: 'system', - privileges: [ - { - description: 'Replace a process level token', - enabled: false, - name: 'SeAssignPrimaryTokenPrivilege', - }, - ], - sid: 'S-1-5-18', - type: 'tokenPrimary', - user: 'SYSTEM', - }, - uptime: 1025, - user: 'SYSTEM', - }, - dll: [ - { - pe: { - architecture: 'x64', - imphash: 'c30d230b81c734e82e86e2e2fe01cd01', - }, - code_signature: { - subject_name: 'Cybereason Inc', - trusted: true, - }, - compile_time: 1534424710, - hash: { - md5: '1f2d082566b0fc5f2c238a5180db7451', - sha1: 'ca85243c0af6a6471bdaa560685c51eefd6dbc0d', - sha256: '8ad40c90a611d36eb8f9eb24fa04f7dbca713db383ff55a03aa0f382e92061a2', - }, - malware_classifier: { - identifier: 'Whitelisted', - score: 0, - threshold: 0, - version: '3.0.0', - }, - mapped_address: 5362483200, - mapped_size: 0, - path: 'C:\\Program Files\\Cybereason ActiveProbe\\AmSvc.exe', - }, - ], }); } const mock: AlertResultList = { diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/index.test.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/index.test.ts index 6903c37d4684..fba1dacb0d3b 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/index.test.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/index.test.ts @@ -7,44 +7,20 @@ import { createStore, Dispatch, Store } from 'redux'; import { ManagementAction, managementListReducer } from './index'; import { EndpointMetadata } from '../../../../../common/types'; +import { EndpointDocGenerator } from '../../../../../common/generate_data'; import { ManagementListState } from '../../types'; import { listData } from './selectors'; describe('endpoint_list store concerns', () => { let store: Store; let dispatch: Dispatch; + const generator = new EndpointDocGenerator(); const createTestStore = () => { store = createStore(managementListReducer); dispatch = store.dispatch; }; const generateEndpoint = (): EndpointMetadata => { - return { - '@timestamp': new Date(1582231151055).toString(), - event: { - created: new Date(0), - }, - endpoint: { - policy: { - id: '', - }, - }, - agent: { - version: '', - id: '', - }, - host: { - id: '', - hostname: '', - ip: [''], - mac: [''], - os: { - name: '', - full: '', - version: '', - variant: '', - }, - }, - }; + return generator.generateEndpointMetadata(new Date().getTime()); }; const loadDataToStore = () => { dispatch({ diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/middleware.test.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/middleware.test.ts index f29e90509785..3b37e0d79bac 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/middleware.test.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/middleware.test.ts @@ -9,6 +9,7 @@ import { coreMock } from '../../../../../../../../src/core/public/mocks'; import { History, createBrowserHistory } from 'history'; import { managementListReducer, managementMiddlewareFactory } from './index'; import { EndpointMetadata, EndpointResultList } from '../../../../../common/types'; +import { EndpointDocGenerator } from '../../../../../common/generate_data'; import { ManagementListState } from '../../types'; import { AppAction } from '../action'; import { listData } from './selectors'; @@ -19,38 +20,14 @@ describe('endpoint list saga', () => { let store: Store; let getState: typeof store['getState']; let dispatch: Dispatch; - let history: History; + const generator = new EndpointDocGenerator(); // https://github.com/elastic/endpoint-app-team/issues/131 const generateEndpoint = (): EndpointMetadata => { - return { - '@timestamp': new Date(1582231151055).toString(), - event: { - created: new Date(0), - }, - endpoint: { - policy: { - id: '', - }, - }, - agent: { - version: '', - id: '', - }, - host: { - id: '', - hostname: '', - ip: [''], - mac: [''], - os: { - name: '', - full: '', - version: '', - variant: '', - }, - }, - }; + return generator.generateEndpointMetadata(new Date().getTime()); }; + + let history: History; const getEndpointListApiResponse = (): EndpointResultList => { return { endpoints: [generateEndpoint()], diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/mock_host_result_list.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/mock_host_result_list.ts index 866e5c59329e..61833d1dfb95 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/mock_host_result_list.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/mock_host_result_list.ts @@ -5,6 +5,7 @@ */ import { EndpointResultList } from '../../../../../common/types'; +import { EndpointDocGenerator } from '../../../../../common/generate_data'; export const mockHostResultList: (options?: { total?: number; @@ -25,33 +26,8 @@ export const mockHostResultList: (options?: { const endpoints = []; for (let index = 0; index < actualCountToReturn; index++) { - endpoints.push({ - '@timestamp': new Date(1582231151055).toString(), - event: { - created: new Date('2020-02-20T20:39:11.055Z'), - }, - endpoint: { - policy: { - id: '00000000-0000-0000-0000-000000000000', - }, - }, - agent: { - version: '6.9.2', - id: '9a87fdac-e6c0-4f27-a25c-e349e7093cb1', - }, - host: { - id: '3ca26fe5-1c7d-42b8-8763-98256d161c9f', - hostname: 'bea-0.example.com', - ip: ['10.154.150.114', '10.43.37.62', '10.217.73.149'], - mac: ['ea-5a-a8-c0-5-95', '7e-d8-fe-7f-b6-4e', '23-31-5d-af-e6-2b'], - os: { - name: 'windows 6.2', - full: 'Windows Server 2012', - version: '6.2', - variant: 'Windows Server Release 2', - }, - }, - }); + const generator = new EndpointDocGenerator('seed'); + endpoints.push(generator.generateEndpointMetadata()); } const mock: EndpointResultList = { endpoints, diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/source_process_accordion.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/source_process_accordion.tsx index 4c961ad4b496..4d921ee39d95 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/source_process_accordion.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/metadata/source_process_accordion.tsx @@ -51,13 +51,13 @@ export const SourceProcessAccordion = memo(({ alertData }: { alertData: Immutabl title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.malwareScore', { defaultMessage: 'MalwareScore', }), - description: alertData.process.malware_classifier.score, + description: alertData.process.malware_classifier?.score || '-', }, { title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.parentProcessID', { defaultMessage: 'Parent Process ID', }), - description: alertData.process.parent.pid, + description: alertData.process.parent?.pid || '-', }, { title: i18n.translate('xpack.endpoint.application.endpoint.alertDetails.signer', { diff --git a/x-pack/plugins/endpoint/server/routes/resolver/utils/normalize.ts b/x-pack/plugins/endpoint/server/routes/resolver/utils/normalize.ts index 4db8ee0bfbce..67a532d949e8 100644 --- a/x-pack/plugins/endpoint/server/routes/resolver/utils/normalize.ts +++ b/x-pack/plugins/endpoint/server/routes/resolver/utils/normalize.ts @@ -21,7 +21,7 @@ export function extractEntityID(event: ResolverEvent) { if (isLegacyData(event)) { return String(event.endgame.unique_pid); } - return event.endpoint.process.entity_id; + return event.process.entity_id; } export function extractParentEntityID(event: ResolverEvent) { @@ -29,5 +29,5 @@ export function extractParentEntityID(event: ResolverEvent) { const ppid = event.endgame.unique_ppid; return ppid && String(ppid); // if unique_ppid is undefined return undefined } - return event.endpoint.process.parent?.entity_id; + return event.process.parent?.entity_id; } diff --git a/yarn.lock b/yarn.lock index 23a2e8e4718d..c84f7932b409 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5169,6 +5169,11 @@ "@types/tough-cookie" "*" form-data "^2.5.0" +"@types/seedrandom@>=2.0.0 <4.0.0": + version "2.4.28" + resolved "https://registry.yarnpkg.com/@types/seedrandom/-/seedrandom-2.4.28.tgz#9ce8fa048c1e8c85cb71d7fe4d704e000226036f" + integrity sha512-SMA+fUwULwK7sd/ZJicUztiPs8F1yCPwF3O23Z9uQ32ME5Ha0NmDK9+QTsYE4O2tHXChzXomSWWeIhCnoN1LqA== + "@types/selenium-webdriver@^4.0.5": version "4.0.5" resolved "https://registry.yarnpkg.com/@types/selenium-webdriver/-/selenium-webdriver-4.0.5.tgz#23041a4948c82daf2df9836e4d2358fec10d3e24"