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 <elasticmachine@users.noreply.github.com> Co-authored-by: Paul Tavares <paul.tavares@elastic.co>
This commit is contained in:
parent
f4b0d5cbbc
commit
4a26f56f31
|
@ -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);
|
||||
|
|
|
@ -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',
|
||||
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',
|
||||
},
|
||||
malware_classification: {
|
||||
identifier: 'endpointpe',
|
||||
score: 1,
|
||||
threshold: 0.66,
|
||||
version: '3.0.33',
|
||||
},
|
||||
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,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -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<Record<string, any>>, doc) => (
|
||||
array.push({ create: { _index: index } }, doc), array
|
||||
),
|
||||
[]
|
||||
);
|
||||
await client.bulk({ body, refresh: 'true' });
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
Ext: {
|
||||
malware_classification: MalwareClassification;
|
||||
temp_file_path: string;
|
||||
code_signature: Array<{
|
||||
trusted: boolean;
|
||||
subject_name: string;
|
||||
}>;
|
||||
};
|
||||
code_signature: {
|
||||
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;
|
||||
|
|
|
@ -74,7 +74,7 @@ export const FileAccordion = memo(({ alertData }: { alertData: Immutable<AlertDa
|
|||
defaultMessage: 'Signer',
|
||||
}
|
||||
),
|
||||
description: alertData.file.code_signature.subject_name,
|
||||
description: alertData.file.Ext.code_signature[0].subject_name,
|
||||
},
|
||||
{
|
||||
title: i18n.translate(
|
||||
|
|
|
@ -56,7 +56,7 @@ export const GeneralAccordion = memo(({ alertData }: { alertData: Immutable<Aler
|
|||
defaultMessage: 'MalwareScore',
|
||||
}
|
||||
),
|
||||
description: alertData.file.malware_classification.score,
|
||||
description: alertData.file.Ext.malware_classification.score,
|
||||
},
|
||||
{
|
||||
title: i18n.translate(
|
||||
|
|
|
@ -73,7 +73,7 @@ export const SourceProcessAccordion = memo(({ alertData }: { alertData: Immutabl
|
|||
defaultMessage: 'MalwareScore',
|
||||
}
|
||||
),
|
||||
description: alertData.process.malware_classification?.score || '-',
|
||||
description: alertData.process.Ext.malware_classification?.score || '-',
|
||||
},
|
||||
{
|
||||
title: i18n.translate(
|
||||
|
@ -91,7 +91,7 @@ export const SourceProcessAccordion = memo(({ alertData }: { alertData: Immutabl
|
|||
defaultMessage: 'Signer',
|
||||
}
|
||||
),
|
||||
description: alertData.process.code_signature.subject_name,
|
||||
description: alertData.process.Ext.code_signature[0].subject_name,
|
||||
},
|
||||
{
|
||||
title: i18n.translate(
|
||||
|
@ -100,7 +100,7 @@ export const SourceProcessAccordion = memo(({ alertData }: { alertData: Immutabl
|
|||
defaultMessage: 'Username',
|
||||
}
|
||||
),
|
||||
description: alertData.process.token.user,
|
||||
description: alertData.process.Ext.token.user,
|
||||
},
|
||||
{
|
||||
title: i18n.translate(
|
||||
|
@ -109,7 +109,7 @@ export const SourceProcessAccordion = memo(({ alertData }: { alertData: Immutabl
|
|||
defaultMessage: 'Domain',
|
||||
}
|
||||
),
|
||||
description: alertData.process.token.domain,
|
||||
description: alertData.process.Ext.token.domain,
|
||||
},
|
||||
];
|
||||
}, [alertData]);
|
||||
|
|
|
@ -20,7 +20,7 @@ export const SourceProcessTokenAccordion = memo(
|
|||
defaultMessage: 'SID',
|
||||
}
|
||||
),
|
||||
description: alertData.process.token.sid,
|
||||
description: alertData.process.Ext.token.sid,
|
||||
},
|
||||
{
|
||||
title: i18n.translate(
|
||||
|
@ -29,7 +29,7 @@ export const SourceProcessTokenAccordion = memo(
|
|||
defaultMessage: 'Integrity Level',
|
||||
}
|
||||
),
|
||||
description: alertData.process.token.integrity_level,
|
||||
description: alertData.process.Ext.token.integrity_level,
|
||||
},
|
||||
];
|
||||
}, [alertData]);
|
||||
|
|
|
@ -204,7 +204,7 @@ export const AlertIndex = memo(() => {
|
|||
} 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;
|
||||
},
|
||||
|
|
|
@ -41,7 +41,7 @@ export const detailsError = (state: Immutable<HostState>) => state.detailsError;
|
|||
* Returns the full policy response from the endpoint after a user modifies a policy.
|
||||
*/
|
||||
const detailsPolicyAppliedResponse = (state: Immutable<HostState>) =>
|
||||
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<HostState>) => string = createSelector(
|
||||
(state) => state.policyResponse,
|
||||
(policyResponse) => {
|
||||
return (policyResponse && policyResponse?.endpoint?.policy?.applied?.status) || '';
|
||||
return (policyResponse && policyResponse?.Endpoint?.policy?.applied?.status) || '';
|
||||
}
|
||||
);
|
||||
|
|
|
@ -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}
|
||||
</EuiLink>
|
||||
</>
|
||||
),
|
||||
|
|
|
@ -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<typeof render>;
|
||||
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();
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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<Record<string, any>>, 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`);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
||||
|
|
|
@ -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)`
|
||||
|
|
|
@ -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<string> = new Set(
|
||||
body.hosts.map((hostInfo: Record<string, any>) => hostInfo.metadata.host.os.variant)
|
||||
body.hosts.map((hostInfo: Record<string, any>) => 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<string> = new Set(
|
||||
body.hosts.map(
|
||||
(hostInfo: Record<string, any>) => hostInfo.metadata.endpoint.policy.applied.status
|
||||
(hostInfo: Record<string, any>) => hostInfo.metadata.Endpoint.policy.applied.status
|
||||
)
|
||||
);
|
||||
|
||||
expect(statuses.size).to.eql(1);
|
||||
expect(Array.from(statuses)).to.eql(['failure']);
|
||||
});
|
||||
|
|
|
@ -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 () => {
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Binary file not shown.
Loading…
Reference in a new issue