From 4a26f56f31e64ddb37cad610d8223c544b1b8dc3 Mon Sep 17 00:00:00 2001 From: Marshall Main <55718608+marshallmain@users.noreply.github.com> Date: Thu, 18 Jun 2020 13:50:53 -0400 Subject: [PATCH] Update endpoint event and alert types (#69292) * start redoing types * finish redoing types * fix bad test * rework tests * fix more types * fix test * Fix endpoints test and render error * add deletePolicyStream to alerts api tests Co-authored-by: Elastic Machine Co-authored-by: Paul Tavares --- .../common/endpoint/generate_data.test.ts | 12 +- .../common/endpoint/generate_data.ts | 189 +++++++++--------- .../common/endpoint/index_data.ts | 92 +++++++++ .../common/endpoint/types.ts | 106 +++++----- .../view/details/metadata/file_accordion.tsx | 2 +- .../details/metadata/general_accordion.tsx | 2 +- .../metadata/source_process_accordion.tsx | 8 +- .../source_process_token_accordion.tsx | 4 +- .../public/endpoint_alerts/view/index.tsx | 2 +- .../pages/endpoint_hosts/store/selectors.ts | 4 +- .../view/details/host_details.tsx | 8 +- .../pages/endpoint_hosts/view/index.test.tsx | 65 ++++-- .../pages/endpoint_hosts/view/index.tsx | 8 +- .../scripts/endpoint/resolver_generator.ts | 74 ++----- .../endpoint/routes/metadata/metadata.test.ts | 2 +- .../apis/endpoint/alerts/index.ts | 111 +++++++--- .../api_integration/apis/endpoint/metadata.ts | 11 +- .../api_integration/apis/endpoint/policy.ts | 2 +- .../endpoint/metadata/api_feature/data.json | 54 +++-- .../es_archives/endpoint/policy/data.json.gz | Bin 1326 -> 1327 bytes 20 files changed, 456 insertions(+), 300 deletions(-) create mode 100644 x-pack/plugins/security_solution/common/endpoint/index_data.ts 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 ba4f2251564e89..d7b653916970fa 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 @@ -47,7 +47,7 @@ describe('data generator', () => { const metadata = generator.generateHostMetadata(timestamp); expect(metadata['@timestamp']).toEqual(timestamp); expect(metadata.event.created).toEqual(timestamp); - expect(metadata.endpoint).not.toBeNull(); + expect(metadata.Endpoint).not.toBeNull(); expect(metadata.agent).not.toBeNull(); expect(metadata.host).not.toBeNull(); }); @@ -57,10 +57,10 @@ describe('data generator', () => { const hostPolicyResponse = generator.generatePolicyResponse(timestamp); expect(hostPolicyResponse['@timestamp']).toEqual(timestamp); expect(hostPolicyResponse.event.created).toEqual(timestamp); - expect(hostPolicyResponse.endpoint).not.toBeNull(); + expect(hostPolicyResponse.Endpoint).not.toBeNull(); expect(hostPolicyResponse.agent).not.toBeNull(); expect(hostPolicyResponse.host).not.toBeNull(); - expect(hostPolicyResponse.endpoint.policy.applied).not.toBeNull(); + expect(hostPolicyResponse.Endpoint.policy.applied).not.toBeNull(); }); it('creates alert event documents', () => { @@ -68,7 +68,7 @@ describe('data generator', () => { const alert = generator.generateAlert(timestamp); expect(alert['@timestamp']).toEqual(timestamp); expect(alert.event.action).not.toBeNull(); - expect(alert.endpoint).not.toBeNull(); + expect(alert.Endpoint).not.toBeNull(); expect(alert.agent).not.toBeNull(); expect(alert.host).not.toBeNull(); expect(alert.process.entity_id).not.toBeNull(); @@ -364,7 +364,9 @@ describe('data generator', () => { it('creates full resolver tree', () => { const alertAncestors = 3; const generations = 2; - const events = [...generator.fullResolverTreeGenerator(alertAncestors, generations)]; + const events = [ + ...generator.fullResolverTreeGenerator({ ancestors: alertAncestors, generations }), + ]; const rootNode = buildResolverTree(events); const visitedEvents = countResolverEvents(rootNode, alertAncestors + generations); expect(visitedEvents).toEqual(events.length); 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 7944d7d365ed89..8c032859967791 100644 --- a/x-pack/plugins/security_solution/common/endpoint/generate_data.ts +++ b/x-pack/plugins/security_solution/common/endpoint/generate_data.ts @@ -10,7 +10,7 @@ import { EndpointEvent, Host, HostMetadata, - HostOS, + OSFields, HostPolicyResponse, HostPolicyResponseActionStatus, PolicyData, @@ -28,38 +28,46 @@ interface EventOptions { processName?: string; } -const Windows: HostOS[] = [ +const Windows: OSFields[] = [ { name: 'windows 10.0', full: 'Windows 10', version: '10.0', - variant: 'Windows Pro', + Ext: { + variant: 'Windows Pro', + }, }, { name: 'windows 10.0', full: 'Windows Server 2016', version: '10.0', - variant: 'Windows Server', + Ext: { + variant: 'Windows Server', + }, }, { name: 'windows 6.2', full: 'Windows Server 2012', version: '6.2', - variant: 'Windows Server', + Ext: { + variant: 'Windows Server', + }, }, { name: 'windows 6.3', full: 'Windows Server 2012R2', version: '6.3', - variant: 'Windows Server Release 2', + Ext: { + variant: 'Windows Server Release 2', + }, }, ]; -const Linux: HostOS[] = []; +const Linux: OSFields[] = []; -const Mac: HostOS[] = []; +const Mac: OSFields[] = []; -const OS: HostOS[] = [...Windows, ...Mac, ...Linux]; +const OS: OSFields[] = [...Windows, ...Mac, ...Linux]; const APPLIED_POLICIES: Array<{ name: string; @@ -186,7 +194,7 @@ interface HostInfo { type: string; }; host: Host; - endpoint: { + Endpoint: { policy: { applied: { id: string; @@ -283,8 +291,8 @@ export class EndpointDocGenerator { * Creates new random policy id for the host to simulate new policy application */ public updatePolicyId() { - this.commonInfo.endpoint.policy.applied.id = this.randomChoice(APPLIED_POLICIES).id; - this.commonInfo.endpoint.policy.applied.status = this.randomChoice([ + this.commonInfo.Endpoint.policy.applied = this.randomChoice(APPLIED_POLICIES); + this.commonInfo.Endpoint.policy.applied.status = this.randomChoice([ HostPolicyResponseActionStatus.success, HostPolicyResponseActionStatus.failure, HostPolicyResponseActionStatus.warning, @@ -310,7 +318,7 @@ export class EndpointDocGenerator { mac: this.randomArray(3, () => this.randomMac()), os: this.randomChoice(OS), }, - endpoint: { + Endpoint: { policy: { applied: this.randomChoice(APPLIED_POLICIES), }, @@ -371,77 +379,88 @@ export class EndpointDocGenerator { sha1: 'fake file sha1', sha256: 'fake file sha256', }, - code_signature: { - trusted: false, - subject_name: 'bad signer', - }, - malware_classification: { - identifier: 'endpointpe', - score: 1, - threshold: 0.66, - version: '3.0.33', + Ext: { + code_signature: [ + { + trusted: false, + subject_name: 'bad signer', + }, + ], + malware_classification: { + identifier: 'endpointpe', + score: 1, + threshold: 0.66, + version: '3.0.33', + }, + temp_file_path: 'C:/temp/fake_malware.exe', }, - temp_file_path: 'C:/temp/fake_malware.exe', }, 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', }, + Ext: { + code_signature: [ + { + trusted: false, + subject_name: 'bad signer', + }, + ], + user: 'SYSTEM', + 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', + }, + }, }, 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_classification: { - identifier: 'Whitelisted', - score: 0, - threshold: 0, - version: '3.0.0', - }, - mapped_address: 5362483200, - mapped_size: 0, + path: 'C:\\Program Files\\Cybereason ActiveProbe\\AmSvc.exe', + Ext: { + compile_time: 1534424710, + mapped_address: 5362483200, + mapped_size: 0, + malware_classification: { + identifier: 'Whitelisted', + score: 0, + threshold: 0, + version: '3.0.0', + }, + }, }, ], }; @@ -561,28 +580,9 @@ export class EndpointDocGenerator { * @param percentTerminated - percent of nodes which will have process termination events * @param alwaysGenMaxChildrenPerNode - flag to always return the max children per node instead of it being a random number of children */ - public *alertsGenerator( - numAlerts: number, - alertAncestors?: number, - childGenerations?: number, - maxChildrenPerNode?: number, - relatedEventsPerNode?: number, - relatedAlertsPerNode?: number, - percentNodesWithRelated?: number, - percentTerminated?: number, - alwaysGenMaxChildrenPerNode?: boolean - ) { + public *alertsGenerator(numAlerts: number, options: TreeOptions = {}) { for (let i = 0; i < numAlerts; i++) { - yield* this.fullResolverTreeGenerator( - alertAncestors, - childGenerations, - maxChildrenPerNode, - relatedEventsPerNode, - relatedAlertsPerNode, - percentNodesWithRelated, - percentTerminated, - alwaysGenMaxChildrenPerNode - ); + yield* this.fullResolverTreeGenerator(options); } } @@ -600,21 +600,12 @@ export class EndpointDocGenerator { * @param percentTerminated - percent of nodes which will have process termination events * @param alwaysGenMaxChildrenPerNode - flag to always return the max children per node instead of it being a random number of children */ - public *fullResolverTreeGenerator( - alertAncestors?: number, - childGenerations?: number, - maxChildrenPerNode?: number, - relatedEventsPerNode?: RelatedEventInfo[] | number, - relatedAlertsPerNode?: number, - percentNodesWithRelated?: number, - percentTerminated?: number, - alwaysGenMaxChildrenPerNode?: boolean - ) { + public *fullResolverTreeGenerator(options: TreeOptions = {}) { const ancestry = this.createAlertEventAncestry( - alertAncestors, - relatedEventsPerNode, - percentNodesWithRelated, - percentTerminated + options.ancestors, + options.relatedEvents, + options.percentWithRelated, + options.percentTerminated ); for (let i = 0; i < ancestry.length; i++) { yield ancestry[i]; @@ -622,13 +613,13 @@ export class EndpointDocGenerator { // ancestry will always have at least 2 elements, and the last element will be the alert yield* this.descendantsTreeGenerator( ancestry[ancestry.length - 1], - childGenerations, - maxChildrenPerNode, - relatedEventsPerNode, - relatedAlertsPerNode, - percentNodesWithRelated, - percentTerminated, - alwaysGenMaxChildrenPerNode + options.generations, + options.children, + options.relatedEvents, + options.relatedAlerts, + options.percentWithRelated, + options.percentTerminated, + options.alwaysGenMaxChildrenPerNode ); } @@ -940,7 +931,7 @@ export class EndpointDocGenerator { host: { id: this.commonInfo.host.id, }, - endpoint: { + Endpoint: { policy: { applied: { actions: [ @@ -1045,7 +1036,7 @@ export class EndpointDocGenerator { status: HostPolicyResponseActionStatus.success, }, ], - id: this.commonInfo.endpoint.policy.applied.id, + id: this.commonInfo.Endpoint.policy.applied.id, response: { configurations: { events: { @@ -1086,9 +1077,9 @@ export class EndpointDocGenerator { ], }, }, - status: this.commonInfo.endpoint.policy.applied.status, + status: this.commonInfo.Endpoint.policy.applied.status, version: policyVersion, - name: this.commonInfo.endpoint.policy.applied.name, + name: this.commonInfo.Endpoint.policy.applied.name, }, }, }, diff --git a/x-pack/plugins/security_solution/common/endpoint/index_data.ts b/x-pack/plugins/security_solution/common/endpoint/index_data.ts new file mode 100644 index 00000000000000..d868ba63b1edcd --- /dev/null +++ b/x-pack/plugins/security_solution/common/endpoint/index_data.ts @@ -0,0 +1,92 @@ +/* + * 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 { Client } from '@elastic/elasticsearch'; +import seedrandom from 'seedrandom'; +import { EndpointDocGenerator, TreeOptions, Event } from './generate_data'; + +export async function indexHostsAndAlerts( + client: Client, + seed: string, + numHosts: number, + numDocs: number, + metadataIndex: string, + policyIndex: string, + eventIndex: string, + alertsPerHost: number, + options: TreeOptions = {} +) { + const random = seedrandom(seed); + for (let i = 0; i < numHosts; i++) { + const generator = new EndpointDocGenerator(random); + await indexHostDocs(numDocs, client, metadataIndex, policyIndex, generator); + await indexAlerts(client, eventIndex, generator, alertsPerHost, options); + } + await client.indices.refresh({ + index: eventIndex, + }); + // TODO: Unclear why the documents are not showing up after the call to refresh. + // Waiting 5 seconds allows the indices to refresh automatically and + // the documents become available in API/integration tests. + await delay(5000); +} + +function delay(ms: number) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + +async function indexHostDocs( + numDocs: number, + client: Client, + metadataIndex: string, + policyIndex: string, + generator: EndpointDocGenerator +) { + const timeBetweenDocs = 6 * 3600 * 1000; // 6 hours between metadata documents + const timestamp = new Date().getTime(); + for (let j = 0; j < numDocs; j++) { + generator.updateHostData(); + generator.updatePolicyId(); + await client.index({ + index: metadataIndex, + body: generator.generateHostMetadata(timestamp - timeBetweenDocs * (numDocs - j - 1)), + op_type: 'create', + }); + await client.index({ + index: policyIndex, + body: generator.generatePolicyResponse(timestamp - timeBetweenDocs * (numDocs - j - 1)), + op_type: 'create', + }); + } +} + +async function indexAlerts( + client: Client, + index: string, + generator: EndpointDocGenerator, + numAlerts: number, + options: TreeOptions = {} +) { + const alertGenerator = generator.alertsGenerator(numAlerts, options); + let result = alertGenerator.next(); + while (!result.done) { + let k = 0; + const resolverDocs: Event[] = []; + while (k < 1000 && !result.done) { + resolverDocs.push(result.value); + result = alertGenerator.next(); + k++; + } + const body = resolverDocs.reduce( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (array: Array>, doc) => ( + array.push({ create: { _index: index } }, doc), array + ), + [] + ); + await client.bulk({ body, refresh: 'true' }); + } +} diff --git a/x-pack/plugins/security_solution/common/endpoint/types.ts b/x-pack/plugins/security_solution/common/endpoint/types.ts index 0341b7593caf06..bd4d8372497f15 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types.ts @@ -173,12 +173,19 @@ export interface HostResultList { } /** - * Operating System metadata for a host. + * Operating System metadata. */ -export interface HostOS { +export interface OSFields { full: string; name: string; version: string; + Ext: OSFieldsExt; +} + +/** + * Extended Operating System metadata. + */ +export interface OSFieldsExt { variant: string; } @@ -190,7 +197,7 @@ export interface Host { hostname: string; ip: string[]; mac: string[]; - os: HostOS; + os: OSFields; } /** @@ -220,27 +227,30 @@ interface MalwareClassification { interface ThreadFields { id: number; - service_name: string; - start: number; - start_address: number; - start_address_module: string; + Ext: { + service_name: string; + start: number; + start_address: number; + start_address_module: string; + }; } interface DllFields { + hash: Hashes; + path: string; pe: { architecture: string; - imphash: string; }; code_signature: { subject_name: string; trusted: boolean; }; - compile_time: number; - hash: Hashes; - malware_classification: MalwareClassification; - mapped_address: number; - mapped_size: number; - path: string; + Ext: { + compile_time: number; + malware_classification: MalwareClassification; + mapped_address: number; + mapped_size: number; + }; } /** @@ -265,7 +275,7 @@ export interface AlertEvent { module: string; type: string; }; - endpoint: { + Endpoint: { policy: { applied: { id: string; @@ -275,12 +285,7 @@ export interface AlertEvent { }; }; process: { - code_signature: { - subject_name: string; - trusted: boolean; - }; command_line?: string; - domain?: string; pid: number; ppid?: number; entity_id: string; @@ -290,29 +295,31 @@ export interface AlertEvent { }; name: string; hash: Hashes; - pe?: { - imphash: string; - }; executable: string; - sid?: string; start: number; - malware_classification?: MalwareClassification; - token: { - domain: string; - type: string; - user: string; - sid: string; - integrity_level: number; - integrity_level_name: string; - privileges?: Array<{ - description: string; - name: string; - enabled: boolean; - }>; - }; thread?: ThreadFields[]; uptime: number; - user: string; + Ext: { + code_signature: Array<{ + subject_name: string; + trusted: boolean; + }>; + malware_classification?: MalwareClassification; + token: { + domain: string; + type: string; + user: string; + sid: string; + integrity_level: number; + integrity_level_name: string; + privileges?: Array<{ + description: string; + name: string; + enabled: boolean; + }>; + }; + user: string; + }; }; file: { owner: string; @@ -323,15 +330,14 @@ export interface AlertEvent { created: number; size: number; hash: Hashes; - pe?: { - imphash: string; - }; - code_signature: { - trusted: boolean; - subject_name: string; + Ext: { + malware_classification: MalwareClassification; + temp_file_path: string; + code_signature: Array<{ + trusted: boolean; + subject_name: string; + }>; }; - malware_classification: MalwareClassification; - temp_file_path: string; }; host: Host; dll?: DllFields[]; @@ -373,7 +379,7 @@ export type HostMetadata = Immutable<{ id: string; }; }; - endpoint: { + Endpoint: { policy: { applied: { id: string; @@ -666,7 +672,7 @@ export interface HostPolicyResponseAppliedAction { message: string; } -export type HostPolicyResponseConfiguration = HostPolicyResponse['endpoint']['policy']['applied']['response']['configurations']; +export type HostPolicyResponseConfiguration = HostPolicyResponse['Endpoint']['policy']['applied']['response']['configurations']; interface HostPolicyResponseConfigurationStatus { status: HostPolicyResponseActionStatus; @@ -711,7 +717,7 @@ export interface HostPolicyResponse { version: string; id: string; }; - endpoint: { + Endpoint: { policy: { applied: { version: string; diff --git a/x-pack/plugins/security_solution/public/endpoint_alerts/view/details/metadata/file_accordion.tsx b/x-pack/plugins/security_solution/public/endpoint_alerts/view/details/metadata/file_accordion.tsx index 6c9df46c9313c4..82800c92e742c1 100644 --- a/x-pack/plugins/security_solution/public/endpoint_alerts/view/details/metadata/file_accordion.tsx +++ b/x-pack/plugins/security_solution/public/endpoint_alerts/view/details/metadata/file_accordion.tsx @@ -74,7 +74,7 @@ export const FileAccordion = memo(({ alertData }: { alertData: Immutable { } else if (columnId === 'archived') { return null; } else if (columnId === 'malware_score') { - return row.file.malware_classification.score; + return row.file.Ext.malware_classification.score; } return null; }, diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/selectors.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/selectors.ts index 5e7cbc0ef58d34..20365b3fe100b9 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/selectors.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/selectors.ts @@ -41,7 +41,7 @@ export const detailsError = (state: Immutable) => state.detailsError; * Returns the full policy response from the endpoint after a user modifies a policy. */ const detailsPolicyAppliedResponse = (state: Immutable) => - state.policyResponse && state.policyResponse.endpoint.policy.applied; + state.policyResponse && state.policyResponse.Endpoint.policy.applied; /** * Returns the response configurations from the endpoint after a user modifies a policy. @@ -179,6 +179,6 @@ export const showView: (state: HostState) => 'policy_response' | 'details' = cre export const policyResponseStatus: (state: Immutable) => string = createSelector( (state) => state.policyResponse, (policyResponse) => { - return (policyResponse && policyResponse?.endpoint?.policy?.applied?.status) || ''; + return (policyResponse && policyResponse?.Endpoint?.policy?.applied?.status) || ''; } ); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/host_details.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/host_details.tsx index 9ec65a5d17898c..f31b54b93851f0 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/host_details.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/host_details.tsx @@ -97,15 +97,15 @@ export const HostDetails = memo(({ details }: { details: HostMetadata }) => { return [ getManagementUrl({ name: 'policyDetails', - policyId: details.endpoint.policy.applied.id, + policyId: details.Endpoint.policy.applied.id, excludePrefix: true, }), getManagementUrl({ name: 'policyDetails', - policyId: details.endpoint.policy.applied.id, + policyId: details.Endpoint.policy.applied.id, }), ]; - }, [details.endpoint.policy.applied.id]); + }, [details.Endpoint.policy.applied.id]); const policyDetailsClickHandler = useNavigateByRouterEventHandler(policyDetailsRoutePath); @@ -123,7 +123,7 @@ export const HostDetails = memo(({ details }: { details: HostMetadata }) => { href={policyDetailsRouteUrl} onClick={policyDetailsClickHandler} > - {details.endpoint.policy.applied.name} + {details.Endpoint.policy.applied.name} ), diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx index e0f797b1430551..af027c2e106c3d 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx @@ -50,13 +50,13 @@ describe('when on the hosts page', () => { }); describe('when list data loads', () => { const generatedPolicyStatuses: Array< - HostInfo['metadata']['endpoint']['policy']['applied']['status'] + HostInfo['metadata']['Endpoint']['policy']['applied']['status'] > = []; let firstPolicyID: string; beforeEach(() => { reactTestingLibrary.act(() => { const hostListData = mockHostResultList({ total: 3 }); - firstPolicyID = hostListData.hosts[0].metadata.endpoint.policy.applied.id; + firstPolicyID = hostListData.hosts[0].metadata.Endpoint.policy.applied.id; [HostStatus.ERROR, HostStatus.ONLINE, HostStatus.OFFLINE].forEach((status, index) => { hostListData.hosts[index] = { metadata: hostListData.hosts[index].metadata, @@ -64,7 +64,7 @@ describe('when on the hosts page', () => { }; }); hostListData.hosts.forEach((item, index) => { - generatedPolicyStatuses[index] = item.metadata.endpoint.policy.applied.status; + generatedPolicyStatuses[index] = item.metadata.Endpoint.policy.applied.status; }); const action: AppAction = { type: 'serverReturnedHostList', @@ -160,9 +160,11 @@ describe('when on the hosts page', () => { overallStatus: HostPolicyResponseActionStatus = HostPolicyResponseActionStatus.success ) => { const policyResponse = docGenerator.generatePolicyResponse(); - policyResponse.endpoint.policy.applied.status = overallStatus; - policyResponse.endpoint.policy.applied.response.configurations.malware.status = overallStatus; - let downloadModelAction = policyResponse.endpoint.policy.applied.actions.find( + const malwareResponseConfigurations = + policyResponse.Endpoint.policy.applied.response.configurations.malware; + policyResponse.Endpoint.policy.applied.status = overallStatus; + malwareResponseConfigurations.status = overallStatus; + let downloadModelAction = policyResponse.Endpoint.policy.applied.actions.find( (action) => action.name === 'download_model' ); @@ -172,19 +174,30 @@ describe('when on the hosts page', () => { message: 'Failed to apply a portion of the configuration (kernel)', status: overallStatus, }; - policyResponse.endpoint.policy.applied.actions.push(downloadModelAction); + policyResponse.Endpoint.policy.applied.actions.push(downloadModelAction); } + if ( overallStatus === HostPolicyResponseActionStatus.failure || overallStatus === HostPolicyResponseActionStatus.warning ) { downloadModelAction.message = 'no action taken'; } - store.dispatch({ - type: 'serverReturnedHostPolicyResponse', - payload: { - policy_response: policyResponse, - }, + + // Make sure that at least one configuration has the above action, else + // we get into an out-of-sync condition + if ( + malwareResponseConfigurations.concerned_actions.indexOf(downloadModelAction.name) === -1 + ) { + malwareResponseConfigurations.concerned_actions.push(downloadModelAction.name); + } + reactTestingLibrary.act(() => { + store.dispatch({ + type: 'serverReturnedHostPolicyResponse', + payload: { + policy_response: policyResponse, + }, + }); }); }; @@ -236,7 +249,7 @@ describe('when on the hosts page', () => { const policyDetailsLink = await renderResult.findByTestId('policyDetailsValue'); expect(policyDetailsLink).not.toBeNull(); expect(policyDetailsLink.getAttribute('href')).toEqual( - `#/management/policy/${hostDetails.metadata.endpoint.policy.applied.id}` + `#/management/policy/${hostDetails.metadata.Endpoint.policy.applied.id}` ); }); @@ -252,7 +265,7 @@ describe('when on the hosts page', () => { }); const changedUrlAction = await userChangedUrlChecker; expect(changedUrlAction.payload.pathname).toEqual( - `/management/policy/${hostDetails.metadata.endpoint.policy.applied.id}` + `/management/policy/${hostDetails.metadata.Endpoint.policy.applied.id}` ); }); @@ -361,6 +374,12 @@ describe('when on the hosts page', () => { describe('when showing host Policy Response panel', () => { let renderResult: ReturnType; beforeEach(async () => { + coreStart.http.post.mockImplementation(async (requestOptions) => { + if (requestOptions.path === '/api/endpoint/metadata') { + return mockHostResultList({ total: 0 }); + } + throw new Error(`POST to '${requestOptions.path}' does not have a mock response!`); + }); renderResult = render(); const policyStatusLink = await renderResult.findByTestId('policyStatusValue'); const userChangedUrlChecker = middlewareSpy.waitForAction('userChangedUrl'); @@ -373,6 +392,8 @@ describe('when on the hosts page', () => { }); }); + afterEach(reactTestingLibrary.cleanup); + it('should hide the host details panel', async () => { const hostDetailsFlyout = await renderResult.queryByTestId('hostDetailsFlyoutBody'); expect(hostDetailsFlyout).toBeNull(); @@ -433,21 +454,29 @@ describe('when on the hosts page', () => { }); }); - it('should show a numbered badge if at least one action failed', () => { + it('should show a numbered badge if at least one action failed', async () => { + const policyResponseActionDispatched = middlewareSpy.waitForAction( + 'serverReturnedHostPolicyResponse' + ); reactTestingLibrary.act(() => { dispatchServerReturnedHostPolicyResponse(HostPolicyResponseActionStatus.failure); }); - const attentionBadge = renderResult.findAllByTestId( + await policyResponseActionDispatched; + const attentionBadge = await renderResult.findAllByTestId( 'hostDetailsPolicyResponseAttentionBadge' ); expect(attentionBadge).not.toBeNull(); }); - it('should show a numbered badge if at least one action has a warning', () => { + it('should show a numbered badge if at least one action has a warning', async () => { + const policyResponseActionDispatched = middlewareSpy.waitForAction( + 'serverReturnedHostPolicyResponse' + ); reactTestingLibrary.act(() => { dispatchServerReturnedHostPolicyResponse(HostPolicyResponseActionStatus.warning); }); - const attentionBadge = renderResult.findAllByTestId( + await policyResponseActionDispatched; + const attentionBadge = await renderResult.findAllByTestId( 'hostDetailsPolicyResponseAttentionBadge' ); expect(attentionBadge).not.toBeNull(); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx index c67c29fbc73a90..149819480855bd 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx @@ -154,13 +154,13 @@ export const HostList = () => { }, }, { - field: 'metadata.endpoint.policy.applied', + field: 'metadata.Endpoint.policy.applied', name: i18n.translate('xpack.securitySolution.endpointList.policy', { defaultMessage: 'Policy', }), truncateText: true, // eslint-disable-next-line react/display-name - render: (policy: HostInfo['metadata']['endpoint']['policy']['applied']) => { + render: (policy: HostInfo['metadata']['Endpoint']['policy']['applied']) => { const toRoutePath = getManagementUrl({ name: 'policyDetails', policyId: policy.id, @@ -181,12 +181,12 @@ export const HostList = () => { }, }, { - field: 'metadata.endpoint.policy.applied', + field: 'metadata.Endpoint.policy.applied', name: i18n.translate('xpack.securitySolution.endpointList.policyStatus', { defaultMessage: 'Policy Status', }), // eslint-disable-next-line react/display-name - render: (policy: HostInfo['metadata']['endpoint']['policy']['applied'], item: HostInfo) => { + render: (policy: HostInfo['metadata']['Endpoint']['policy']['applied'], item: HostInfo) => { const toRoutePath = getManagementUrl({ name: 'endpointPolicyResponse', selected_host: item.metadata.host.id, diff --git a/x-pack/plugins/security_solution/scripts/endpoint/resolver_generator.ts b/x-pack/plugins/security_solution/scripts/endpoint/resolver_generator.ts index e4460b337960fd..542991a927f7ab 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/resolver_generator.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/resolver_generator.ts @@ -8,10 +8,9 @@ import * as path from 'path'; import yargs from 'yargs'; import * as url from 'url'; import fetch from 'node-fetch'; -import seedrandom from 'seedrandom'; import { Client, ClientOptions } from '@elastic/elasticsearch'; import { ResponseError } from '@elastic/elasticsearch/lib/errors'; -import { EndpointDocGenerator, Event } from '../../common/endpoint/generate_data'; +import { indexHostsAndAlerts } from '../../common/endpoint/index_data'; main(); @@ -201,59 +200,26 @@ async function main() { seed = Math.random().toString(); console.log(`No seed supplied, using random seed: ${seed}`); } - const random = seedrandom(seed); const startTime = new Date().getTime(); - for (let i = 0; i < argv.numHosts; i++) { - const generator = new EndpointDocGenerator(random); - const timeBetweenDocs = 6 * 3600 * 1000; // 6 hours between metadata documents - - const timestamp = new Date().getTime(); - for (let j = 0; j < argv.numDocs; j++) { - generator.updateHostData(); - generator.updatePolicyId(); - await client.index({ - index: argv.metadataIndex, - body: generator.generateHostMetadata(timestamp - timeBetweenDocs * (argv.numDocs - j - 1)), - op_type: 'create', - }); - await client.index({ - index: argv.policyIndex, - body: generator.generatePolicyResponse( - timestamp - timeBetweenDocs * (argv.numDocs - j - 1) - ), - op_type: 'create', - }); + await indexHostsAndAlerts( + client, + seed, + argv.numHosts, + argv.numDocs, + argv.metadataIndex, + argv.policyIndex, + argv.eventIndex, + argv.alertsPerHost, + { + ancestors: argv.ancestors, + generations: argv.generations, + children: argv.children, + relatedEvents: argv.relatedEvents, + relatedAlerts: argv.relatedAlerts, + percentWithRelated: argv.percentWithRelated, + percentTerminated: argv.percentTerminated, + alwaysGenMaxChildrenPerNode: argv.maxChildrenPerNode, } - - const alertGenerator = generator.alertsGenerator( - argv.alertsPerHost, - argv.ancestors, - argv.generations, - argv.children, - argv.relatedEvents, - argv.relatedAlerts, - argv.percentWithRelated, - argv.percentTerminated, - argv.maxChildrenPerNode - ); - let result = alertGenerator.next(); - while (!result.done) { - let k = 0; - const resolverDocs: Event[] = []; - while (k < 1000 && !result.done) { - resolverDocs.push(result.value); - result = alertGenerator.next(); - k++; - } - const body = resolverDocs.reduce( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (array: Array>, doc) => ( - array.push({ create: { _index: argv.eventIndex } }, doc), array - ), - [] - ); - await client.bulk({ body, refresh: 'true' }); - } - } + ); console.log(`Creating and indexing documents took: ${new Date().getTime() - startTime}ms`); } diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata.test.ts index 80626bbdb6e7fc..92835dc5329ce3 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata.test.ts @@ -237,7 +237,7 @@ describe('test endpoint route', () => { expect(routeConfig.options).toEqual({ authRequired: true }); expect(mockResponse.ok).toBeCalled(); const result = mockResponse.ok.mock.calls[0][0]?.body as HostInfo; - expect(result).toHaveProperty('metadata.endpoint'); + expect(result).toHaveProperty('metadata.Endpoint'); expect(result.host_status).toEqual(HostStatus.ONLINE); }); diff --git a/x-pack/test/api_integration/apis/endpoint/alerts/index.ts b/x-pack/test/api_integration/apis/endpoint/alerts/index.ts index 12c5857f9db398..ed287834761d8b 100644 --- a/x-pack/test/api_integration/apis/endpoint/alerts/index.ts +++ b/x-pack/test/api_integration/apis/endpoint/alerts/index.ts @@ -7,12 +7,19 @@ import expect from '@kbn/expect/expect.js'; import { FtrProviderContext } from '../../../ftr_provider_context'; import { AlertData } from '../../../../../plugins/security_solution/common/endpoint_alerts/types'; import { eventsIndexPattern } from '../../../../../plugins/security_solution/common/endpoint/constants'; -import { deleteEventsStream, deleteMetadataStream } from '../data_stream_helper'; +import { + deleteEventsStream, + deleteMetadataStream, + deletePolicyStream, +} from '../data_stream_helper'; +import { indexHostsAndAlerts } from '../../../../../plugins/security_solution/common/endpoint/index_data'; /** * The number of alert documents in the es archive. */ -const numberOfAlertsInFixture = 12; +const numberOfHosts = 3; +const numberOfAlertsPerHost = 4; +const numberOfAlertsInFixture = numberOfHosts * numberOfAlertsPerHost; /** * The default number of entries returned when no page_size is specified. @@ -57,10 +64,9 @@ const ES_QUERY_MISSING = { }; export default function ({ getService }: FtrProviderContext) { - const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); const es = getService('legacyEs'); - + const client = getService('es'); const nextPrevPrefixQuery = "query=(language:kuery,query:'')"; const nextPrevPrefixDateRange = "date_range=(from:'2018-01-10T00:00:00.000Z',to:now)"; const nextPrevPrefixSort = 'sort=@timestamp'; @@ -73,8 +79,17 @@ export default function ({ getService }: FtrProviderContext) { describe('Endpoint alert API', () => { describe('when data is in elasticsearch', () => { before(async () => { - await esArchiver.load('endpoint/alerts/api_feature', { useCreate: true }); - await esArchiver.load('endpoint/alerts/host_api_feature', { useCreate: true }); + await indexHostsAndAlerts( + client, + 'alerts-seed', + numberOfHosts, + 1, + 'metrics-endpoint.metadata-default-1', + 'metrics-endpoint.policy-default-1', + 'events-endpoint-1', + numberOfAlertsPerHost + ); + const res = await es.search({ index: eventsIndexPattern, body: ES_QUERY_MISSING, @@ -85,7 +100,11 @@ export default function ({ getService }: FtrProviderContext) { after(async () => { // the endpoint uses data streams and es archiver does not support deleting them at the moment so we need // to do it manually - await Promise.all([deleteEventsStream(getService), deleteMetadataStream(getService)]); + await Promise.all([ + deleteEventsStream(getService), + deleteMetadataStream(getService), + deletePolicyStream(getService), + ]); }); it('should not support POST requests', async () => { @@ -159,7 +178,7 @@ export default function ({ getService }: FtrProviderContext) { expect(body.message).to.contain('Value must be equal to or greater than [1]'); }); - it('should return links to the next and previous pages using cursor-based pagination', async () => { + it('should return working link to the next page using cursor-based pagination', async () => { const { body } = await supertest .get('/api/endpoint/alerts?page_index=0') .set('kbn-xsrf', 'xxx') @@ -170,51 +189,75 @@ export default function ({ getService }: FtrProviderContext) { expect(body.next).to.eql( `/api/endpoint/alerts?${nextPrevPrefix}&after=${lastTimestampFirstPage}&after=${lastEventIdFirstPage}` ); - }); - it('should return data using `next` link', async () => { - const { body } = await supertest - .get( - `/api/endpoint/alerts?${nextPrevPrefix}&after=1584044338719&after=66008e21-2493-4b15-a937-939ea228064a` - ) + const { body: nextBody } = await supertest + .get(body.next) .set('kbn-xsrf', 'xxx') .expect(200); - expect(body.alerts.length).to.eql(defaultPageSize); - const firstTimestampNextPage = body.alerts[0]['@timestamp']; - const firstEventIdNextPage = body.alerts[0].event.id; - expect(body.prev).to.eql( + expect(nextBody.alerts.length).to.eql(2); + const firstTimestampNextPage = nextBody.alerts[0]['@timestamp']; + const firstEventIdNextPage = nextBody.alerts[0].event.id; + expect(nextBody.prev).to.eql( `/api/endpoint/alerts?${nextPrevPrefix}&before=${firstTimestampNextPage}&before=${firstEventIdNextPage}` ); }); - it('should return data using `prev` link', async () => { + it('should return working link to the prev page using cursor-based pagination', async () => { const { body } = await supertest - .get( - `/api/endpoint/alerts?${nextPrevPrefix}&before=1542789412000&before=823d814d-fa0c-4e53-a94c-f6b296bb965b` - ) + .get('/api/endpoint/alerts?page_index=1') .set('kbn-xsrf', 'xxx') .expect(200); - expect(body.alerts.length).to.eql(10); + expect(body.alerts.length).to.eql(2); + const firstTimestamp = body.alerts[0]['@timestamp']; + const firstEventId = body.alerts[0].event.id; + expect(body.prev).to.eql( + `/api/endpoint/alerts?${nextPrevPrefix}&before=${firstTimestamp}&before=${firstEventId}` + ); + + const { body: prevBody } = await supertest + .get(body.prev) + .set('kbn-xsrf', 'xxx') + .expect(200); + expect(prevBody.alerts.length).to.eql(10); + const lastTimestampFirstPage = prevBody.alerts[9]['@timestamp']; + const lastEventIdFirstPage = prevBody.alerts[9].event.id; + expect(prevBody.next).to.eql( + `/api/endpoint/alerts?${nextPrevPrefix}&after=${lastTimestampFirstPage}&after=${lastEventIdFirstPage}` + ); }); it('should return no results when `before` is requested past beginning of first page', async () => { const { body } = await supertest + .get('/api/endpoint/alerts') + .set('kbn-xsrf', 'xxx') + .expect(200); + + const { body: emptyBody } = await supertest .get( - `/api/endpoint/alerts?${nextPrevPrefix}&before=1584044338726&before=5ff1a4ec-758e-49e7-89aa-2c6821fe6b54` + `/api/endpoint/alerts?${nextPrevPrefix}&before=${ + body.alerts[0]['@timestamp'] + 1 + }&before=5ff1a4ec-758e-49e7-89aa-2c6821fe6b54` ) .set('kbn-xsrf', 'xxx') .expect(200); - expect(body.alerts.length).to.eql(0); + expect(emptyBody.alerts.length).to.eql(0); }); it('should return no results when `after` is requested past end of last page, descending', async () => { const { body } = await supertest + .get('/api/endpoint/alerts?page_index=1') + .set('kbn-xsrf', 'xxx') + .expect(200); + + const { body: emptyBody } = await supertest .get( - `/api/endpoint/alerts?${nextPrevPrefix}&after=1584044338612&after=6d75d498-3cca-45ad-a304-525b95ae0412` + `/api/endpoint/alerts?${nextPrevPrefix}&after=${ + body.alerts[1]['@timestamp'] - 1 + }&after=6d75d498-3cca-45ad-a304-525b95ae0412` ) .set('kbn-xsrf', 'xxx') .expect(200); - expect(body.alerts.length).to.eql(0); + expect(emptyBody.alerts.length).to.eql(0); }); it('alerts api should return data using `before` by custom sort parameter, descending', async () => { @@ -346,7 +389,12 @@ export default function ({ getService }: FtrProviderContext) { }); it('should filter results of alert data using rison-encoded filters', async () => { - const hostname = 'Host-abmfhmc5ku'; + const { body: firstBody } = await supertest + .get('/api/endpoint/alerts?page_index=0') + .set('kbn-xsrf', 'xxx') + .expect(200); + + const hostname = firstBody.alerts[0].host.hostname; const { body } = await supertest .get( `/api/endpoint/alerts?filters=!((%27%24state%27%3A(store%3AappState)%2Cmeta%3A(alias%3A!n%2Cdisabled%3A!f%2Ckey%3Ahost.hostname%2Cnegate%3A!f%2Cparams%3A(query%3A${hostname})%2Ctype%3Aphrase)%2Cquery%3A(match_phrase%3A(host.hostname%3A${hostname}))))` @@ -361,7 +409,12 @@ export default function ({ getService }: FtrProviderContext) { }); it('should filter results of alert data using KQL', async () => { - const agentID = '7cf9f7a3-28a6-4d1e-bb45-005aa28f18d0'; + const { body: firstBody } = await supertest + .get('/api/endpoint/alerts?page_index=0') + .set('kbn-xsrf', 'xxx') + .expect(200); + + const agentID = firstBody.alerts[0].agent.id; const { body } = await supertest .get( `/api/endpoint/alerts?query=(language%3Akuery%2Cquery%3A%27agent.id%20%3A%20"${agentID}"%27)` diff --git a/x-pack/test/api_integration/apis/endpoint/metadata.ts b/x-pack/test/api_integration/apis/endpoint/metadata.ts index 61f294cbd6f9c8..41531269ddeb95 100644 --- a/x-pack/test/api_integration/apis/endpoint/metadata.ts +++ b/x-pack/test/api_integration/apis/endpoint/metadata.ts @@ -160,18 +160,18 @@ export default function ({ getService }: FtrProviderContext) { expect(body.request_page_index).to.eql(0); }); - it('metadata api should return page based on host.os.variant filter.', async () => { + it('metadata api should return page based on host.os.Ext.variant filter.', async () => { const variantValue = 'Windows Pro'; const { body } = await supertest .post('/api/endpoint/metadata') .set('kbn-xsrf', 'xxx') .send({ - filter: `host.os.variant:${variantValue}`, + filter: `host.os.Ext.variant:${variantValue}`, }) .expect(200); expect(body.total).to.eql(2); const resultOsVariantValue: Set = new Set( - body.hosts.map((hostInfo: Record) => hostInfo.metadata.host.os.variant) + body.hosts.map((hostInfo: Record) => hostInfo.metadata.host.os.Ext.variant) ); expect(Array.from(resultOsVariantValue)).to.eql([variantValue]); expect(body.hosts.length).to.eql(2); @@ -204,15 +204,14 @@ export default function ({ getService }: FtrProviderContext) { .post('/api/endpoint/metadata') .set('kbn-xsrf', 'xxx') .send({ - filter: `not endpoint.policy.applied.status:success`, + filter: `not Endpoint.policy.applied.status:success`, }) .expect(200); const statuses: Set = new Set( body.hosts.map( - (hostInfo: Record) => hostInfo.metadata.endpoint.policy.applied.status + (hostInfo: Record) => hostInfo.metadata.Endpoint.policy.applied.status ) ); - expect(statuses.size).to.eql(1); expect(Array.from(statuses)).to.eql(['failure']); }); diff --git a/x-pack/test/api_integration/apis/endpoint/policy.ts b/x-pack/test/api_integration/apis/endpoint/policy.ts index 711762cc20abbd..e33423d172567b 100644 --- a/x-pack/test/api_integration/apis/endpoint/policy.ts +++ b/x-pack/test/api_integration/apis/endpoint/policy.ts @@ -27,7 +27,7 @@ export default function ({ getService }: FtrProviderContext) { .expect(200); expect(body.policy_response.host.id).to.eql(expectedHostId); - expect(body.policy_response.endpoint.policy).to.not.be(undefined); + expect(body.policy_response.Endpoint.policy).to.not.be(undefined); }); it('should return not found if host has no policy response', async () => { diff --git a/x-pack/test/functional/es_archives/endpoint/metadata/api_feature/data.json b/x-pack/test/functional/es_archives/endpoint/metadata/api_feature/data.json index a8d868ebbec15f..1434aae60c9674 100644 --- a/x-pack/test/functional/es_archives/endpoint/metadata/api_feature/data.json +++ b/x-pack/test/functional/es_archives/endpoint/metadata/api_feature/data.json @@ -15,7 +15,7 @@ "id": "11488bae-880b-4e7b-8d28-aac2aa9de816" } }, - "endpoint": { + "Endpoint": { "policy": { "applied": { "name": "Default", @@ -44,7 +44,9 @@ "full": "Windows 10", "name": "windows 10.0", "version": "10.0", - "variant" : "Windows Pro" + "Ext": { + "variant" : "Windows Pro" + } } } } @@ -68,7 +70,7 @@ "id": "92ac1ce0-e1f7-409e-8af6-f17e97b1fc71" } }, - "endpoint": { + "Endpoint": { "policy": { "applied": { "name": "Default", @@ -96,7 +98,9 @@ "full": "Windows Server 2016", "name": "windows 10.0", "version": "10.0", - "variant" : "Windows Server" + "Ext": { + "variant" : "Windows Server" + } } } } @@ -120,7 +124,7 @@ "id": "023fa40c-411d-4188-a941-4147bfadd095" } }, - "endpoint": { + "Endpoint": { "policy": { "applied": { "name": "Default", @@ -146,7 +150,9 @@ "full": "Windows 10", "name": "windows 10.0", "version": "10.0", - "variant" : "Windows Pro" + "Ext": { + "variant" : "Windows Pro" + } } } } @@ -170,7 +176,7 @@ "id": "11488bae-880b-4e7b-8d28-aac2aa9de816" } }, - "endpoint": { + "Endpoint": { "policy": { "applied": { "name": "Default", @@ -199,7 +205,9 @@ "full": "Windows Server 2016", "name": "windows 10.0", "version": "10.0", - "variant" : "Windows Server 2016" + "Ext": { + "variant" : "Windows Server 2016" + } } } } @@ -223,7 +231,7 @@ "id": "92ac1ce0-e1f7-409e-8af6-f17e97b1fc71" } }, - "endpoint": { + "Endpoint": { "policy": { "applied": { "name": "Default", @@ -250,7 +258,9 @@ "full": "Windows Server 2012", "name": "windows 6.2", "version": "6.2", - "variant" : "Windows Server 2012" + "Ext": { + "variant" : "Windows Server 2012" + } } } } @@ -274,7 +284,7 @@ "id": "023fa40c-411d-4188-a941-4147bfadd095" } }, - "endpoint": { + "Endpoint": { "policy": { "applied": { "name": "With Eventing", @@ -301,7 +311,9 @@ "full": "Windows Server 2012", "name": "windows 6.2", "version": "6.2", - "variant" : "Windows Server 2012" + "Ext": { + "variant" : "Windows Server 2012" + } } } } @@ -325,7 +337,7 @@ "id": "11488bae-880b-4e7b-8d28-aac2aa9de816" } }, - "endpoint": { + "Endpoint": { "policy": { "applied": { "name": "With Eventing", @@ -353,7 +365,9 @@ "full": "Windows Server 2012R2", "name": "windows 6.3", "version": "6.3", - "variant" : "Windows Server 2012 R2" + "Ext": { + "variant" : "Windows Server 2012 R2" + } } } } @@ -377,7 +391,7 @@ "id": "92ac1ce0-e1f7-409e-8af6-f17e97b1fc71" } }, - "endpoint": { + "Endpoint": { "policy": { "applied": { "name": "Default", @@ -404,7 +418,9 @@ "full": "Windows Server 2012R2", "name": "windows 6.3", "version": "6.3", - "variant" : "Windows Server 2012 R2" + "Ext": { + "variant" : "Windows Server 2012 R2" + } } } } @@ -428,7 +444,7 @@ "id": "023fa40c-411d-4188-a941-4147bfadd095" } }, - "endpoint": { + "Endpoint": { "policy": { "applied": { "name": "With Eventing", @@ -455,7 +471,9 @@ "full": "Windows Server 2012", "name": "windows 6.2", "version": "6.2", - "variant" : "Windows Server 2012" + "Ext": { + "variant" : "Windows Server 2012" + } } } } diff --git a/x-pack/test/functional/es_archives/endpoint/policy/data.json.gz b/x-pack/test/functional/es_archives/endpoint/policy/data.json.gz index f380785f021bbfa49f17206df174a6a44c9d69df..d9fcf03f43f37d02e93a8c20bf8a1ba3d33ee9ce 100644 GIT binary patch delta 1297 zcmV+s1@8K;3a<(WABzYGXDsTG2Qhyv3X&Xec9HL1QZMTn+kw2@gDyc#%?y7W4qyLo zHvlZM=a?;i0t+h0;#M_0;c%~7zR4f*C#UM`hx^a5Zmxg*`}y~ePre=g_VM#WZmujM z%DyP09m^6PD&9;w(T zN3%`SzO78~mPJ{^r)I;thKX&t5Su=MNd6B!gfO&S>iJYB7Pi>p_UQG*l9Y=`x#~-8 zsliYX94-yb&@`mmOKnlwzSMm)lD2zp#Xy5DAx<+++HUu-NuCcWKZF$3KmaU6%7(N44aKr%V1wD&=NA|eX-cAv0K zIkJ@Dggk6yd&F|L0WYA6B2DG6)eu{bfQa2L1$9^o(mf&4IM|2bb7Q{hNU}V#y<%IG zmWz(fOV_CZ{m7Dtg(ng3F%Kn5CXQgBbbXa5kbctk5~5ISw>;XOMEZZDk(Oq-Vis-KlU(3K>*p21G!tMVN(805JU5fA~$9$38@;k@x(v2(ni2v*LC zysVT{y(zl-!IAUao=Ms~h(U~#RART_rerEliZkbZou4$6r7VBRH+6N^)TQ_{hy;+= zS5B+*r)ZaFfTRm$_9p^QnA(%N*PwG!+gkQ23|MzAMboFbpuMP8`!wx*M{psbf>N0YK0JV?@VKbbacORR-Mk}tfaAg zQ4L+E7cKmeQ}~tZL=l0gLDS9g<@V2jVdntoC#c19d;<*F{X68`$glK^)Sabg)hmP? zYWxj!+3-g1j3Z+mID1AcN3YG-PR?r&u{R~BKZNfvL17&;E72pJBw zsNEFu9(jM1`xPSBrrv6MOb;R*R1Q&K`kt$lL(irVDd7#9h7AobbSlnSJDs)D0mIG#FkmsM1U8){Iw5-up^piLh%y7ZmhD5w z4|HrI!|`?PJ8Pp|3a$zVABzYG;-lV?2Qhyn3X*JZcH!?{QV(0!Beny1y9Zr@n3@^>X88Vp zy8~dBza(t-6PQs!X7{RLi^Gj-`6hqJpPZ_%A09s^y1D%I@0Z^{Zhbra?c?XC!dzLz zlzmY~5zA9fGRR_@2p;G2M1-8YK*|EV33I5|9+}vrY#To)nJ z(QMPS?`sphW^vx|Ij~_}!^E~+h)thBB>#sVLKxaE^?a%m3tMb%z-CS|KC zvy9=CJgsDV%nG+2FQ6txmdUhLiYJbMh$zayAuI*ih7f5SY{KxRFJXAvJU4<$-2j$oj2eU&JXe$w_DqEM{YJYJtg`lEkQmS(wX|FJ6- z;c$mjc9!c?%)TUYrnVOT**Fp zAb0$0CO{nnEdN@9NYHcp;`MN4(94O`S@r@jC~75QAOes-vBPGBi^g~5&hdUESUD#0 zvQiE8s_g0qM=ol6A!+j<1_@3xiG2b$CR24%TsZE_;-sN0V`+b}sjIW*Fcp6Wu>kV= z%4vQ6lxEOEYBwq9 zPNdI4D0V;Aj(LCby9Hf6(H@M2r+MZQE)Ca%=s{b+#R4JdKA6S=>F9CcELxq*QOOed zqUyU&&ra|MPT^Os6J-RVlBTQU%k7^Y!`1=NEzk+i@eR;p_ivwbqrTEFR(F<;Rc9g; zP{VJa>(U#%GY;h1bM_2a4qlsYU7Xh*Vs9!=H-+zz^1y#1uRrB1mzz*D&Wz~J=Cjj& z@TFhdHD95l4*ylVQFk+I88p->4`E#+%!8gz42W#chpu5VXfVy8f$1RMcG~VSj*9*F zPo6)4r5gDb^bxN|huU^zIwA)(FXX0PW$!5D`G(^$Y(cD343S}(5EBDK$H5dK!=V z-Tv-Q#ky2G4O`mj*Y$_nYNxhkPTJ|Doi-v(+Uca7PTJ`>u#BdW!IdMAEuS7BwK!EDK`d8<3fr#W1lw7U)f(?tj!q{{wPJ;^$W? F005q4hvEPL