[SECURITY SOLUTION] EMT-401: add policy data to metadata and fix tests (#68582)

[SECURITY SOLUTION] EMT-401: add policy data to metadata and fix tests
This commit is contained in:
nnamdifrankie 2020-06-12 12:45:20 -04:00 committed by GitHub
parent fef7ff859d
commit c3d784c4f7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 174 additions and 279 deletions

View file

@ -61,14 +61,20 @@ const Mac: HostOS[] = [];
const OS: HostOS[] = [...Windows, ...Mac, ...Linux];
const POLICIES: Array<{ name: string; id: string }> = [
const APPLIED_POLICIES: Array<{
name: string;
id: string;
status: HostPolicyResponseActionStatus;
}> = [
{
name: 'Default',
id: '00000000-0000-0000-0000-000000000000',
status: HostPolicyResponseActionStatus.success,
},
{
name: 'With Eventing',
id: 'C2A9093E-E289-4C0A-AA44-8C32A414FA7A',
status: HostPolicyResponseActionStatus.success,
},
];
@ -181,7 +187,11 @@ interface HostInfo {
host: Host;
endpoint: {
policy: {
id: string;
applied: {
id: string;
status: HostPolicyResponseActionStatus;
name: string;
};
};
};
}
@ -271,7 +281,12 @@ export class EndpointDocGenerator {
* Creates new random policy id for the host to simulate new policy application
*/
public updatePolicyId() {
this.commonInfo.endpoint.policy.id = this.randomChoice(POLICIES).id;
this.commonInfo.endpoint.policy.applied.id = this.randomChoice(APPLIED_POLICIES).id;
this.commonInfo.endpoint.policy.applied.status = this.randomChoice([
HostPolicyResponseActionStatus.success,
HostPolicyResponseActionStatus.failure,
HostPolicyResponseActionStatus.warning,
]);
}
private createHostData(): HostInfo {
@ -293,7 +308,9 @@ export class EndpointDocGenerator {
os: this.randomChoice(OS),
},
endpoint: {
policy: this.randomChoice(POLICIES),
policy: {
applied: this.randomChoice(APPLIED_POLICIES),
},
},
};
}
@ -974,7 +991,7 @@ export class EndpointDocGenerator {
status: HostPolicyResponseActionStatus.success,
},
],
id: this.commonInfo.endpoint.policy.id,
id: this.commonInfo.endpoint.policy.applied.id,
response: {
configurations: {
events: {
@ -1015,8 +1032,9 @@ export class EndpointDocGenerator {
],
},
},
status: this.randomHostPolicyResponseActionStatus(),
status: this.commonInfo.endpoint.policy.applied.status,
version: policyVersion,
name: this.commonInfo.endpoint.policy.applied.name,
},
},
},

View file

@ -253,7 +253,11 @@ export type AlertEvent = Immutable<{
};
endpoint: {
policy: {
id: string;
applied: {
id: string;
status: HostPolicyResponseActionStatus;
name: string;
};
};
};
process: {
@ -357,7 +361,11 @@ export type HostMetadata = Immutable<{
};
endpoint: {
policy: {
id: string;
applied: {
id: string;
status: HostPolicyResponseActionStatus;
name: string;
};
};
};
agent: {
@ -700,6 +708,7 @@ export interface HostPolicyResponse {
applied: {
version: string;
id: string;
name: string;
status: HostPolicyResponseActionStatus;
actions: HostPolicyResponseAppliedAction[];
response: {

View file

@ -86,7 +86,7 @@ export const HostDetails = memo(({ details }: { details: HostMetadata }) => {
title: i18n.translate('xpack.securitySolution.endpoint.host.details.policy', {
defaultMessage: 'Policy',
}),
description: details.endpoint.policy.id,
description: details.endpoint.policy.applied.id,
},
{
title: i18n.translate('xpack.securitySolution.endpoint.host.details.policyStatus', {
@ -138,10 +138,10 @@ export const HostDetails = memo(({ details }: { details: HostMetadata }) => {
},
];
}, [
details.endpoint.policy.id,
details.host.ip,
details.host.hostname,
details.agent.version,
details.endpoint.policy.applied.id,
details.host.hostname,
details.host.ip,
policyStatus,
policyResponseUri,
policyStatusClickHandler,

View file

@ -27,7 +27,6 @@ import {
} from '../../../../common/endpoint/types';
import { SearchResponse } from 'elasticsearch';
import { registerEndpointRoutes } from './index';
import * as data from '../../test_data/all_metadata_data.json';
import {
createMockAgentService,
createMockMetadataIndexPatternRetriever,
@ -37,6 +36,7 @@ import { AgentService } from '../../../../../ingest_manager/server';
import Boom from 'boom';
import { EndpointAppContextService } from '../../endpoint_app_context_services';
import { createMockConfig } from '../../../lib/detection_engine/routes/__mocks__';
import { EndpointDocGenerator } from '../../../../common/endpoint/generate_data';
describe('test endpoint route', () => {
let routerMock: jest.Mocked<IRouter>;
@ -78,10 +78,7 @@ describe('test endpoint route', () => {
it('test find the latest of all endpoints', async () => {
const mockRequest = httpServerMock.createKibanaRequest({});
const response: SearchResponse<HostMetadata> = (data as unknown) as SearchResponse<
HostMetadata
>;
const response = createSearchResponse(new EndpointDocGenerator().generateHostMetadata());
mockScopedClient.callAsCurrentUser.mockImplementationOnce(() => Promise.resolve(response));
[routeConfig, routeHandler] = routerMock.post.mock.calls.find(([{ path }]) =>
path.startsWith('/api/endpoint/metadata')
@ -97,8 +94,8 @@ describe('test endpoint route', () => {
expect(routeConfig.options).toEqual({ authRequired: true });
expect(mockResponse.ok).toBeCalled();
const endpointResultList = mockResponse.ok.mock.calls[0][0]?.body as HostResultList;
expect(endpointResultList.hosts.length).toEqual(2);
expect(endpointResultList.total).toEqual(2);
expect(endpointResultList.hosts.length).toEqual(1);
expect(endpointResultList.total).toEqual(1);
expect(endpointResultList.request_page_index).toEqual(0);
expect(endpointResultList.request_page_size).toEqual(10);
});
@ -119,7 +116,7 @@ describe('test endpoint route', () => {
mockAgentService.getAgentStatusById = jest.fn().mockReturnValue('error');
mockScopedClient.callAsCurrentUser.mockImplementationOnce(() =>
Promise.resolve((data as unknown) as SearchResponse<HostMetadata>)
Promise.resolve(createSearchResponse(new EndpointDocGenerator().generateHostMetadata()))
);
[routeConfig, routeHandler] = routerMock.post.mock.calls.find(([{ path }]) =>
path.startsWith('/api/endpoint/metadata')
@ -138,8 +135,8 @@ describe('test endpoint route', () => {
expect(routeConfig.options).toEqual({ authRequired: true });
expect(mockResponse.ok).toBeCalled();
const endpointResultList = mockResponse.ok.mock.calls[0][0]?.body as HostResultList;
expect(endpointResultList.hosts.length).toEqual(2);
expect(endpointResultList.total).toEqual(2);
expect(endpointResultList.hosts.length).toEqual(1);
expect(endpointResultList.total).toEqual(1);
expect(endpointResultList.request_page_index).toEqual(10);
expect(endpointResultList.request_page_size).toEqual(10);
});
@ -162,7 +159,7 @@ describe('test endpoint route', () => {
mockAgentService.getAgentStatusById = jest.fn().mockReturnValue('error');
mockScopedClient.callAsCurrentUser.mockImplementationOnce(() =>
Promise.resolve((data as unknown) as SearchResponse<HostMetadata>)
Promise.resolve(createSearchResponse(new EndpointDocGenerator().generateHostMetadata()))
);
[routeConfig, routeHandler] = routerMock.post.mock.calls.find(([{ path }]) =>
path.startsWith('/api/endpoint/metadata')
@ -194,8 +191,8 @@ describe('test endpoint route', () => {
expect(routeConfig.options).toEqual({ authRequired: true });
expect(mockResponse.ok).toBeCalled();
const endpointResultList = mockResponse.ok.mock.calls[0][0]?.body as HostResultList;
expect(endpointResultList.hosts.length).toEqual(2);
expect(endpointResultList.total).toEqual(2);
expect(endpointResultList.hosts.length).toEqual(1);
expect(endpointResultList.total).toEqual(1);
expect(endpointResultList.request_page_index).toEqual(10);
expect(endpointResultList.request_page_size).toEqual(10);
});
@ -203,25 +200,9 @@ describe('test endpoint route', () => {
describe('Endpoint Details route', () => {
it('should return 404 on no results', async () => {
const mockRequest = httpServerMock.createKibanaRequest({ params: { id: 'BADID' } });
mockScopedClient.callAsCurrentUser.mockImplementationOnce(() =>
Promise.resolve({
took: 3,
timed_out: false,
_shards: {
total: 1,
successful: 1,
skipped: 0,
failed: 0,
},
hits: {
total: {
value: 9,
relation: 'eq',
},
max_score: null,
hits: [],
},
})
Promise.resolve(createSearchResponse())
);
mockAgentService.getAgentStatusById = jest.fn().mockReturnValue('error');
[routeConfig, routeHandler] = routerMock.get.mock.calls.find(([{ path }]) =>
@ -241,13 +222,10 @@ describe('test endpoint route', () => {
});
it('should return a single endpoint with status online', async () => {
const response = createSearchResponse(new EndpointDocGenerator().generateHostMetadata());
const mockRequest = httpServerMock.createKibanaRequest({
// eslint-disable-next-line @typescript-eslint/no-explicit-any
params: { id: (data as any).hits.hits[0]._id },
params: { id: response.hits.hits[0]._id },
});
const response: SearchResponse<HostMetadata> = (data as unknown) as SearchResponse<
HostMetadata
>;
mockAgentService.getAgentStatusById = jest.fn().mockReturnValue('online');
mockScopedClient.callAsCurrentUser.mockImplementationOnce(() => Promise.resolve(response));
[routeConfig, routeHandler] = routerMock.get.mock.calls.find(([{ path }]) =>
@ -269,9 +247,7 @@ describe('test endpoint route', () => {
});
it('should return a single endpoint with status error when AgentService throw 404', async () => {
const response: SearchResponse<HostMetadata> = (data as unknown) as SearchResponse<
HostMetadata
>;
const response = createSearchResponse(new EndpointDocGenerator().generateHostMetadata());
const mockRequest = httpServerMock.createKibanaRequest({
params: { id: response.hits.hits[0]._id },
@ -299,9 +275,7 @@ describe('test endpoint route', () => {
});
it('should return a single endpoint with status error when status is not offline or online', async () => {
const response: SearchResponse<HostMetadata> = (data as unknown) as SearchResponse<
HostMetadata
>;
const response = createSearchResponse(new EndpointDocGenerator().generateHostMetadata());
const mockRequest = httpServerMock.createKibanaRequest({
params: { id: response.hits.hits[0]._id },
@ -327,3 +301,59 @@ describe('test endpoint route', () => {
});
});
});
function createSearchResponse(hostMetadata?: HostMetadata): SearchResponse<HostMetadata> {
return ({
took: 15,
timed_out: false,
_shards: {
total: 1,
successful: 1,
skipped: 0,
failed: 0,
},
hits: {
total: {
value: 5,
relation: 'eq',
},
max_score: null,
hits: hostMetadata
? [
{
_index: 'metrics-endpoint.metadata-default-1',
_id: '8FhM0HEBYyRTvb6lOQnw',
_score: null,
_source: hostMetadata,
sort: [1588337587997],
inner_hits: {
most_recent: {
hits: {
total: {
value: 2,
relation: 'eq',
},
max_score: null,
hits: [
{
_index: 'metrics-endpoint.metadata-default-1',
_id: 'W6Vo1G8BYQH1gtPUgYkC',
_score: null,
_source: hostMetadata,
sort: [1579816615336],
},
],
},
},
},
},
]
: [],
},
aggregations: {
total: {
value: 1,
},
},
} as unknown) as SearchResponse<HostMetadata>;
}

View file

@ -1,216 +0,0 @@
{
"took": 343,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 4,
"relation": "eq"
},
"max_score": null,
"hits": [
{
"_index": "metadata-endpoint-default-1",
"_id": "WqVo1G8BYQH1gtPUgYkC",
"_score": null,
"_source": {
"@timestamp": 1579816615336,
"event": {
"created": "2020-01-23T21:56:55.336Z"
},
"elastic": {
"agent": {
"id": "56a75650-3c8a-4e4f-ac17-6dd729c650e2"
}
},
"endpoint": {
"policy": {
"id": "C2A9093E-E289-4C0A-AA44-8C32A414FA7A"
}
},
"agent": {
"version": "6.8.3",
"id": "56a75650-3c8a-4e4f-ac17-6dd729c650e2",
"name": "Elastic Endpoint"
},
"host": {
"id": "7141a48b-e19f-4ae3-89a0-6e7179a84265",
"hostname": "larimer-0.example.com",
"ip": "10.21.48.136",
"mac": "77-be-30-f0-e8-d6",
"architecture": "x86_64",
"os": {
"name": "windows 6.2",
"full": "Windows Server 2012",
"version": "6.2",
"variant": "Windows Server"
}
}
},
"fields": {
"host.id.keyword": ["7141a48b-e19f-4ae3-89a0-6e7179a84265"]
},
"sort": [1579816615336],
"inner_hits": {
"most_recent": {
"hits": {
"total": {
"value": 2,
"relation": "eq"
},
"max_score": null,
"hits": [
{
"_index": "metadata-endpoint-default-1",
"_id": "WqVo1G8BYQH1gtPUgYkC",
"_score": null,
"_source": {
"@timestamp": 1579816615336,
"event": {
"created": "2020-01-23T21:56:55.336Z"
},
"elastic": {
"agent": {
"id": "56a75650-3c8a-4e4f-ac17-6dd729c650e2"
}
},
"endpoint": {
"policy": {
"id": "C2A9093E-E289-4C0A-AA44-8C32A414FA7A"
}
},
"agent": {
"version": "6.8.3",
"id": "56a75650-3c8a-4e4f-ac17-6dd729c650e2",
"name": "Elastic Endpoint"
},
"host": {
"id": "7141a48b-e19f-4ae3-89a0-6e7179a84265",
"hostname": "larimer-0.example.com",
"ip": "10.21.48.136",
"mac": "77-be-30-f0-e8-d6",
"architecture": "x86_64",
"os": {
"name": "windows 6.2",
"full": "Windows Server 2012",
"version": "6.2",
"variant": "Windows Server"
}
}
},
"sort": [1579816615336]
}
]
}
}
}
},
{
"_index": "metadata-endpoint-default-1",
"_id": "W6Vo1G8BYQH1gtPUgYkC",
"_score": null,
"_source": {
"@timestamp": 1579816615336,
"event": {
"created": "2020-01-23T21:56:55.336Z"
},
"elastic": {
"agent": {
"id": "c2d84d8f-d355-40de-8b54-5d318d4d1312"
}
},
"endpoint": {
"policy": {
"id": "C2A9093E-E289-4C0A-AA44-8C32A414FA7A"
}
},
"agent": {
"version": "6.4.3",
"id": "c2d84d8f-d355-40de-8b54-5d318d4d1312",
"name": "Elastic Endpoint"
},
"host": {
"id": "f35ec6c1-6562-45b1-818f-2f14c0854adf",
"hostname": "hildebrandt-6.example.com",
"ip": "10.53.92.84",
"mac": "af-f1-8f-51-25-2a",
"architecture": "x86_64",
"os": {
"name": "windows 10.0",
"full": "Windows 10",
"version": "10.0",
"variant": "Windows Pro"
}
}
},
"fields": {
"host.id.keyword": ["f35ec6c1-6562-45b1-818f-2f14c0854adf"]
},
"sort": [1579816615336],
"inner_hits": {
"most_recent": {
"hits": {
"total": {
"value": 2,
"relation": "eq"
},
"max_score": null,
"hits": [
{
"_index": "metadata-endpoint-default-1",
"_id": "W6Vo1G8BYQH1gtPUgYkC",
"_score": null,
"_source": {
"@timestamp": 1579816615336,
"event": {
"created": "2020-01-23T21:56:55.336Z"
},
"elastic": {
"agent": {
"id": "c2d84d8f-d355-40de-8b54-5d318d4d1312"
}
},
"endpoint": {
"policy": {
"id": "C2A9093E-E289-4C0A-AA44-8C32A414FA7A"
}
},
"agent": {
"version": "6.4.3",
"id": "c2d84d8f-d355-40de-8b54-5d318d4d1312",
"name": "Elastic Endpoint"
},
"host": {
"id": "f35ec6c1-6562-45b1-818f-2f14c0854adf",
"hostname": "hildebrandt-6.example.com",
"ip": "10.53.92.84",
"mac": "af-f1-8f-51-25-2a",
"architecture": "x86_64",
"os": {
"name": "windows 10.0",
"full": "Windows 10",
"version": "10.0",
"variant": "Windows Pro"
}
}
},
"sort": [1579816615336]
}
]
}
}
}
}
]
},
"aggregations": {
"total": {
"value": 2
}
}
}

View file

@ -199,6 +199,24 @@ export default function ({ getService }: FtrProviderContext) {
expect(body.request_page_index).to.eql(0);
});
it('metadata api should return the latest event for all the events where policy status is not success', async () => {
const { body } = await supertest
.post('/api/endpoint/metadata')
.set('kbn-xsrf', 'xxx')
.send({
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
)
);
expect(statuses.size).to.eql(1);
expect(Array.from(statuses)).to.eql(['failure']);
});
it('metadata api should return the endpoint based on the elastic agent id, and status should be error', async () => {
const targetEndpointId = 'fc0ff548-feba-41b6-8367-65e8790d0eaf';
const targetElasticAgentId = '023fa40c-411d-4188-a941-4147bfadd095';

View file

@ -17,7 +17,11 @@
},
"endpoint": {
"policy": {
"id": "C2A9093E-E289-4C0A-AA44-8C32A414FA7A"
"applied": {
"name": "Default",
"id": "C2A9093E-E289-4C0A-AA44-8C32A414FA7A",
"status": "failure"
}
}
},
"event": {
@ -66,7 +70,11 @@
},
"endpoint": {
"policy": {
"id": "C2A9093E-E289-4C0A-AA44-8C32A414FA7A"
"applied": {
"name": "Default",
"id": "C2A9093E-E289-4C0A-AA44-8C32A414FA7A",
"status": "success"
}
}
},
"event": {
@ -114,7 +122,11 @@
},
"endpoint": {
"policy": {
"id": "00000000-0000-0000-0000-000000000000"
"applied": {
"name": "Default",
"id": "00000000-0000-0000-0000-000000000000",
"status": "failure"
}
}
},
"event": {
@ -160,7 +172,11 @@
},
"endpoint": {
"policy": {
"id": "C2A9093E-E289-4C0A-AA44-8C32A414FA7A"
"applied": {
"name": "Default",
"id": "C2A9093E-E289-4C0A-AA44-8C32A414FA7A",
"status": "failure"
}
}
},
"event": {
@ -209,7 +225,11 @@
},
"endpoint": {
"policy": {
"id": "C2A9093E-E289-4C0A-AA44-8C32A414FA7A"
"applied": {
"name": "Default",
"id": "C2A9093E-E289-4C0A-AA44-8C32A414FA7A",
"status": "success"
}
}
},
"event": {
@ -256,7 +276,11 @@
},
"endpoint": {
"policy": {
"id": "00000000-0000-0000-0000-000000000000"
"applied": {
"name": "With Eventing",
"id": "00000000-0000-0000-0000-000000000000",
"status": "failure"
}
}
},
"event": {
@ -303,7 +327,11 @@
},
"endpoint": {
"policy": {
"id": "00000000-0000-0000-0000-000000000000"
"applied": {
"name": "With Eventing",
"id": "00000000-0000-0000-0000-000000000000",
"status": "failure"
}
}
},
"event": {
@ -351,7 +379,11 @@
},
"endpoint": {
"policy": {
"id": "C2A9093E-E289-4C0A-AA44-8C32A414FA7A"
"applied": {
"name": "Default",
"id": "C2A9093E-E289-4C0A-AA44-8C32A414FA7A",
"status": "success"
}
}
},
"event": {
@ -398,7 +430,11 @@
},
"endpoint": {
"policy": {
"id": "00000000-0000-0000-0000-000000000000"
"applied": {
"name": "With Eventing",
"id": "00000000-0000-0000-0000-000000000000",
"status": "success"
}
}
},
"event": {