Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EMT-401: add policy data to metadata and fix tests #68582

Merged
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 20 additions & 6 deletions x-pack/plugins/security_solution/common/endpoint/generate_data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,16 @@ const Mac: HostOS[] = [];

const OS: HostOS[] = [...Windows, ...Mac, ...Linux];

const POLICIES: Array<{ name: string; id: string }> = [
const 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,
},
];

Expand Down Expand Up @@ -181,7 +183,11 @@ interface HostInfo {
host: Host;
endpoint: {
policy: {
id: string;
applied: {
id: string;
status: HostPolicyResponseActionStatus;
name: string;
};
};
};
}
Expand Down Expand Up @@ -271,7 +277,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(POLICIES).id;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Maybe change the name of this function since it's updating more than just the policy id right?

this.commonInfo.endpoint.policy.applied.status = this.randomChoice([
HostPolicyResponseActionStatus.success,
HostPolicyResponseActionStatus.failure,
HostPolicyResponseActionStatus.warning,
]);
}

private createHostData(): HostInfo {
Expand All @@ -293,7 +304,9 @@ export class EndpointDocGenerator {
os: this.randomChoice(OS),
},
endpoint: {
policy: this.randomChoice(POLICIES),
policy: {
applied: this.randomChoice(POLICIES),
},
},
};
}
Expand Down Expand Up @@ -974,7 +987,7 @@ export class EndpointDocGenerator {
status: HostPolicyResponseActionStatus.success,
},
],
id: this.commonInfo.endpoint.policy.id,
id: this.commonInfo.endpoint.policy.applied.id,
response: {
configurations: {
events: {
Expand Down Expand Up @@ -1015,8 +1028,9 @@ export class EndpointDocGenerator {
],
},
},
status: this.randomHostPolicyResponseActionStatus(),
status: this.commonInfo.endpoint.policy.applied.status,
version: policyVersion,
name: this.commonInfo.endpoint.policy.applied.name,
},
},
},
Expand Down
13 changes: 11 additions & 2 deletions x-pack/plugins/security_solution/common/endpoint/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,11 @@ export type AlertEvent = Immutable<{
};
endpoint: {
policy: {
id: string;
applied: {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm I don't think alerts will have this structure. The mapping for alerts looks like:

endpoint:
  fields:
    policy: 
      fields:
        id: {}
    artifact: {}

based on here: https://github.com/elastic/endpoint-package/blob/master/custom_subsets/elastic_endpoint/events/malware_event.yaml#L26

Do we need to update the mapping?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

id: string;
status: HostPolicyResponseActionStatus;
name: string;
};
};
};
process: {
Expand Down Expand Up @@ -357,7 +361,11 @@ export type HostMetadata = Immutable<{
};
endpoint: {
policy: {
id: string;
applied: {
id: string;
status: HostPolicyResponseActionStatus;
name: string;
};
};
};
agent: {
Expand Down Expand Up @@ -700,6 +708,7 @@ export interface HostPolicyResponse {
applied: {
version: string;
id: string;
name: string;
status: HostPolicyResponseActionStatus;
actions: HostPolicyResponseAppliedAction[];
response: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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', {
Expand Down Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! 👍

import {
createMockAgentService,
createMockMetadataIndexPatternRetriever,
Expand All @@ -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>;
Expand Down Expand Up @@ -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')
Expand All @@ -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);
});
Expand All @@ -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')
Expand All @@ -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);
});
Expand All @@ -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')
Expand Down Expand Up @@ -194,34 +191,18 @@ 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);
});

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 }]) =>
Expand All @@ -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 }]) =>
Expand All @@ -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 },
Expand Down Expand Up @@ -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 },
Expand All @@ -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>;
}
Loading