From 1bb16ed0c0143489f411661675a7b5528b93812e Mon Sep 17 00:00:00 2001 From: Paul Tavares Date: Tue, 21 Apr 2020 15:25:42 -0400 Subject: [PATCH 01/12] Add policyStatus to store + drive UI color/label from status value --- .../endpoint/store/hosts/reducer.ts | 1 + .../endpoint/store/hosts/selectors.ts | 10 ++++++++++ .../public/applications/endpoint/types.ts | 2 ++ .../view/hosts/details/host_details.tsx | 19 +++++++++++++++---- 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/reducer.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/reducer.ts index 298e819645dbe..71cf251b1d86e 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/reducer.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/reducer.ts @@ -16,6 +16,7 @@ const initialState = (): HostListState => { loading: false, detailsError: undefined, details: undefined, + policyResponse: undefined, location: undefined, }; }; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/selectors.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/selectors.ts index 03cdba8505800..940d3cb621279 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/selectors.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/selectors.ts @@ -66,3 +66,13 @@ export const showView: (state: HostListState) => 'policy_response' | 'details' = return searchParams.show === 'policy_response' ? 'policy_response' : 'details'; } ); + +/** + * Returns the Policy Response overall status + */ +export const policyResponseStatus: (state: Immutable) => string = createSelector( + state => state.policyResponse, + policyResponse => { + return (policyResponse && policyResponse?.endpoint?.policy?.status) || ''; + } +); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/types.ts b/x-pack/plugins/endpoint/public/applications/endpoint/types.ts index f407d32cb3b42..2c3fffd502192 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/types.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/types.ts @@ -96,6 +96,8 @@ export interface HostListState { loading: boolean; detailsError?: ServerApiError; details?: Immutable; + /** Holds the Policy Response for the Host currently being displayed in the details */ + policyResponse?: { [key: string]: any }; // Will be Typed once we have an API for it location?: Immutable; } diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/details/host_details.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/details/host_details.tsx index 32c69426b03f3..4710c8f3115f0 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/details/host_details.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/details/host_details.tsx @@ -22,7 +22,7 @@ import { FormattedDateAndTime } from '../../formatted_date_time'; import { LinkToApp } from '../../components/link_to_app'; import { useHostListSelector, useHostLogsUrl } from '../hooks'; import { urlFromQueryParams } from '../url_from_query_params'; -import { uiQueryParams } from '../../../store/hosts/selectors'; +import { policyResponseStatus, uiQueryParams } from '../../../store/hosts/selectors'; const HostIds = styled(EuiListGroupItem)` margin-top: 0; @@ -31,9 +31,18 @@ const HostIds = styled(EuiListGroupItem)` } `; +const POLICY_STATUS_TO_HEALTH_COLOR = Object.freeze({ + success: 'success', + warning: 'warning', + failed: 'danger', +}); + export const HostDetails = memo(({ details }: { details: HostMetadata }) => { const { appId, appPath, url } = useHostLogsUrl(details.host.id); const queryParams = useHostListSelector(uiQueryParams); + const policyStatus = useHostListSelector( + policyResponseStatus + ) as keyof typeof POLICY_STATUS_TO_HEALTH_COLOR; const history = useHistory(); const detailsResultsUpper = useMemo(() => { return [ @@ -79,7 +88,7 @@ export const HostDetails = memo(({ details }: { details: HostMetadata }) => { defaultMessage: 'Policy Status', }), description: ( - + {/* eslint-disable-next-line @elastic/eui/href-or-on-click */} { }} > @@ -129,6 +139,7 @@ export const HostDetails = memo(({ details }: { details: HostMetadata }) => { details.host.ip, history, policyResponseUri, + policyStatus, ]); return ( From 80824d4729f13c9c2c60a8c25179b30875648bb4 Mon Sep 17 00:00:00 2001 From: Paul Tavares Date: Tue, 21 Apr 2020 16:11:10 -0400 Subject: [PATCH 02/12] Added action, middleware, reducer for policy response processing --- .../applications/endpoint/store/hosts/action.ts | 5 +++++ .../applications/endpoint/store/hosts/middleware.ts | 13 +++++++++++++ .../applications/endpoint/store/hosts/reducer.ts | 5 +++++ 3 files changed, 23 insertions(+) diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/action.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/action.ts index 21871ec8ca849..c608f1cd41ff2 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/action.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/action.ts @@ -22,6 +22,10 @@ interface ServerFailedToReturnHostDetails { payload: ServerApiError; } +interface ServerReturnedHostPolicyResponse { + type: 'serverReturnedHostPolicyResponse'; + payload: { [key: string]: any }; // FIXME: replace with API response type once implemented +} interface UserPaginatedHostList { type: 'userPaginatedHostList'; payload: HostListPagination; @@ -31,4 +35,5 @@ export type HostAction = | ServerReturnedHostList | ServerReturnedHostDetails | ServerFailedToReturnHostDetails + | ServerReturnedHostPolicyResponse | UserPaginatedHostList; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/middleware.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/middleware.ts index 83e11f5408bcd..1559d32abd467 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/middleware.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/middleware.ts @@ -39,6 +39,19 @@ export const hostMiddlewareFactory: ImmutableMiddlewareFactory = type: 'serverReturnedHostDetails', payload: response, }); + // FIXME: once we have the API implementation in place, we should call it parallel with the above api call and then dispatch this with the results of the second call + dispatch({ + type: 'serverReturnedHostPolicyResponse', + payload: { + policy_response: { + endpoint: { + policy: { + status: 'success', + }, + }, + }, + }, + }); } catch (error) { dispatch({ type: 'serverFailedToReturnHostDetails', diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/reducer.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/reducer.ts index 71cf251b1d86e..25229ddf49956 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/reducer.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/reducer.ts @@ -50,6 +50,11 @@ export const hostListReducer: ImmutableReducer = ( ...state, detailsError: action.payload, }; + } else if (action.type === 'serverReturnedHostPolicyResponse') { + return { + ...state, + policyResponse: action.payload.policy_response, + }; } else if (action.type === 'userPaginatedHostList') { return { ...state, From 7bdb19cda33f3a3b2458b5a2b3d573172754f31a Mon Sep 17 00:00:00 2001 From: Paul Tavares Date: Tue, 21 Apr 2020 17:36:27 -0400 Subject: [PATCH 03/12] An attempt at typing the PolicyResponse message --- .../plugins/endpoint/common/generate_data.ts | 98 +++++++++- x-pack/plugins/endpoint/common/types.ts | 170 ++++++++++++++++++ .../endpoint/store/hosts/selectors.ts | 2 +- 3 files changed, 268 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/endpoint/common/generate_data.ts b/x-pack/plugins/endpoint/common/generate_data.ts index 1978d780f54f5..9cc37c7f92afb 100644 --- a/x-pack/plugins/endpoint/common/generate_data.ts +++ b/x-pack/plugins/endpoint/common/generate_data.ts @@ -6,7 +6,16 @@ import uuid from 'uuid'; import seedrandom from 'seedrandom'; -import { AlertEvent, EndpointEvent, HostMetadata, OSFields, HostFields, PolicyData } from './types'; +import { + AlertEvent, + EndpointEvent, + HostFields, + HostMetadata, + OSFields, + PolicyData, + PolicyResponse, + PolicyResponseActionStatus, +} from './types'; import { factory as policyFactory } from './models/policy_config'; export type Event = AlertEvent | EndpointEvent; @@ -486,6 +495,93 @@ export class EndpointDocGenerator { }; } + generatePolicyResponse(): PolicyResponse { + return { + '@timestamp': new Date().getTime(), + elastic: { + agent: { + id: 'c2a9093e-e289-4c0a-aa44-8c32a414fa7a', + }, + }, + ecs: { + version: '1.0.0', + }, + event: { + created: '2015-01-01T12:10:30Z', + kind: 'policy_response', + }, + agent: { + version: '6.0.0-rc2', + id: '8a4f500d', + }, + endpoint: { + artifacts: { + 'global-manifest': { + version: '1.2.3', + sha256: 'abcdef', + }, + 'endpointpe-v4-windows': { + version: '1.2.3', + sha256: 'abcdef', + }, + 'user-whitelist-windows': { + version: '1.2.3', + sha256: 'abcdef', + }, + 'global-whitelist-windows': { + version: '1.2.3', + sha256: 'abcdef', + }, + }, + policy: { + applied: { + version: '1.0.0', + id: '17d4b81d-9940-4b64-9de5-3e03ef1fb5cf', + status: PolicyResponseActionStatus.success, + response: { + configurations: { + malware: { + status: PolicyResponseActionStatus.success, + concerned_actions: ['download_model', 'workflow', 'a-custom-future-action'], + }, + events: { + status: PolicyResponseActionStatus.success, + concerned_actions: ['ingest_events_config', 'workflow'], + }, + logging: { + status: PolicyResponseActionStatus.success, + concerned_actions: ['configure_elasticsearch_connection'], + }, + streaming: { + status: PolicyResponseActionStatus.success, + concerned_actions: [ + 'detect_file_open_events', + 'download_global_artifacts', + 'my-custom-value', + ], + }, + }, + actions: { + download_model: { + status: PolicyResponseActionStatus.success, + message: 'model downloaded', + }, + ingest_events_config: { + status: PolicyResponseActionStatus.success, + message: 'no action taken', + }, + workflow: { + status: PolicyResponseActionStatus.success, + message: 'the flow worked well', + }, + }, + }, + }, + }, + }, + }; + } + private randomN(n: number): number { return Math.floor(this.random() * n); } diff --git a/x-pack/plugins/endpoint/common/types.ts b/x-pack/plugins/endpoint/common/types.ts index 49f8ebbd580d8..b5291825c08ae 100644 --- a/x-pack/plugins/endpoint/common/types.ts +++ b/x-pack/plugins/endpoint/common/types.ts @@ -573,3 +573,173 @@ export type NewPolicyData = NewDatasource & { } ]; }; + +/** + * the possible status for actions, configurations and overall Policy Response + */ +export enum PolicyResponseActionStatus { + success = 'success', + failure = 'failure', + warning = 'warning', +} + +/** + * The details of a given action + */ +interface PolicyResponseActionDetails { + status: PolicyResponseActionStatus; + message: string; +} + +/** + * A known list of possible Endpoint actions + */ +interface PolicyResponseActions { + download_model: PolicyResponseActionDetails; + ingest_events_config: PolicyResponseActionDetails; + workflow: PolicyResponseActionDetails; + configure_elasticsearch_connection: PolicyResponseActionDetails; + configure_kernel: PolicyResponseActionDetails; + configure_logging: PolicyResponseActionDetails; + configure_malware: PolicyResponseActionDetails; + connect_kernel: PolicyResponseActionDetails; + detect_file_open_events: PolicyResponseActionDetails; + detect_file_write_events: PolicyResponseActionDetails; + detect_image_load_events: PolicyResponseActionDetails; + detect_process_events: PolicyResponseActionDetails; + download_global_artifacts: PolicyResponseActionDetails; + load_config: PolicyResponseActionDetails; + load_malware_model: PolicyResponseActionDetails; + read_elasticsearch_config: PolicyResponseActionDetails; + read_events_config: PolicyResponseActionDetails; + read_kernel_config: PolicyResponseActionDetails; + read_logging_config: PolicyResponseActionDetails; + read_malware_config: PolicyResponseActionDetails; + // The list of possible Actions will change rapidly, so the below entry will allow + // them without us defining them here statically + [key: string]: PolicyResponseActionDetails; +} + +interface PolicyResponseConfigurationStatus { + status: PolicyResponseActionStatus; + concerned_actions: Array; +} + +/** + * Information about the applying of a policy to a given host + */ +export interface PolicyResponse { + '@timestamp': number; + elastic: { + agent: { + id: string; + }; + }; + ecs: { + version: string; + }; + event: { + created: string; + kind: string; + }; + agent: { + version: string; + id: string; + }; + endpoint: { + artifacts: {}; + policy: { + applied: { + version: string; + id: string; + status: PolicyResponseActionStatus; + response: { + configurations: { + malware: PolicyResponseConfigurationStatus; + events: PolicyResponseConfigurationStatus; + logging: PolicyResponseConfigurationStatus; + streaming: PolicyResponseConfigurationStatus; + }; + actions: Partial; + }; + }; + }; + }; +} + +/* +{ + "@timestamp": "a formatted timestamp", + "elastic": { + "agent": { + "id": "c2a9093e-e289-4c0a-aa44-8c32a414fa7a" //Elastic Agent ID + } + }, + "ecs": { + "version": "1.0.0" + }, + "event": { + "created": "2015-01-01T12:10:30Z", + "kind": "policy_response" //Thoughts on this name? + }, + "agent": { //Elastic Endpoint information + "version": "6.0.0-rc2", + "id": "8a4f500d", + }, + "endpoint": { + "artifacts": { + "global-manifest": { + "version": "1.2.3", + "sha256": "abcdef" + }, + "endpointpe-v4-windows": { + "version": "1.2.3", + "sha256": "abcdef", + }, + "user-whitelist-windows": { + "version": "1.2.3", + "sha256": "abcdef", + }, + "global-whitelist-windows": { + "version": "1.2.3", + "sha256": "abcdef", + }, + }, + "policy": { + "applied": { + "version": "1.0.0", + "id": "17d4b81d-9940-4b64-9de5-3e03ef1fb5cf", + "status": "failed", + "response": { + "configurations": { + "malware": { + "status": "success", + "concerned_actions": [ "download_model", "workflow", ] //Contains all actions taken regardless of status + }, + "eventing": { + "status": "failed", + "concerned_actions": [ "ingest_events_config", "workflow", ] //Contains all actions taken regardless of status + } + }, + "actions": { + "download_model": { + "status": "success", + "message": "model downloaded" + }, + "ingest_events_config": { + "status": "failure", + "message": "no action taken" + }, + "workflow": { + "status": "success", + "message": "the flow worked well" + }, + // ... + } + } + } + } + } +} + + */ diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/selectors.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/selectors.ts index 940d3cb621279..89b77fdc32de4 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/selectors.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/selectors.ts @@ -73,6 +73,6 @@ export const showView: (state: HostListState) => 'policy_response' | 'details' = export const policyResponseStatus: (state: Immutable) => string = createSelector( state => state.policyResponse, policyResponse => { - return (policyResponse && policyResponse?.endpoint?.policy?.status) || ''; + return (policyResponse && policyResponse?.endpoint?.policy?.applied?.status) || ''; } ); From 51299f9f61729fc8636e294c9793e63f229b1e45 Mon Sep 17 00:00:00 2001 From: Paul Tavares Date: Tue, 21 Apr 2020 17:39:55 -0400 Subject: [PATCH 04/12] remove commented out sample message --- x-pack/plugins/endpoint/common/types.ts | 77 ------------------------- 1 file changed, 77 deletions(-) diff --git a/x-pack/plugins/endpoint/common/types.ts b/x-pack/plugins/endpoint/common/types.ts index b5291825c08ae..70c1ed10cda22 100644 --- a/x-pack/plugins/endpoint/common/types.ts +++ b/x-pack/plugins/endpoint/common/types.ts @@ -666,80 +666,3 @@ export interface PolicyResponse { }; }; } - -/* -{ - "@timestamp": "a formatted timestamp", - "elastic": { - "agent": { - "id": "c2a9093e-e289-4c0a-aa44-8c32a414fa7a" //Elastic Agent ID - } - }, - "ecs": { - "version": "1.0.0" - }, - "event": { - "created": "2015-01-01T12:10:30Z", - "kind": "policy_response" //Thoughts on this name? - }, - "agent": { //Elastic Endpoint information - "version": "6.0.0-rc2", - "id": "8a4f500d", - }, - "endpoint": { - "artifacts": { - "global-manifest": { - "version": "1.2.3", - "sha256": "abcdef" - }, - "endpointpe-v4-windows": { - "version": "1.2.3", - "sha256": "abcdef", - }, - "user-whitelist-windows": { - "version": "1.2.3", - "sha256": "abcdef", - }, - "global-whitelist-windows": { - "version": "1.2.3", - "sha256": "abcdef", - }, - }, - "policy": { - "applied": { - "version": "1.0.0", - "id": "17d4b81d-9940-4b64-9de5-3e03ef1fb5cf", - "status": "failed", - "response": { - "configurations": { - "malware": { - "status": "success", - "concerned_actions": [ "download_model", "workflow", ] //Contains all actions taken regardless of status - }, - "eventing": { - "status": "failed", - "concerned_actions": [ "ingest_events_config", "workflow", ] //Contains all actions taken regardless of status - } - }, - "actions": { - "download_model": { - "status": "success", - "message": "model downloaded" - }, - "ingest_events_config": { - "status": "failure", - "message": "no action taken" - }, - "workflow": { - "status": "success", - "message": "the flow worked well" - }, - // ... - } - } - } - } - } -} - - */ From 00a0bea2ee788a16d785cf8933c3f5f1a4510ac5 Mon Sep 17 00:00:00 2001 From: Paul Tavares Date: Wed, 22 Apr 2020 09:21:31 -0400 Subject: [PATCH 05/12] Use Policy Response Type in store concerns --- .../plugins/endpoint/common/generate_data.ts | 29 ++++++-- x-pack/plugins/endpoint/common/types.ts | 71 ++++++++++--------- .../endpoint/store/hosts/action.ts | 4 +- .../endpoint/store/hosts/middleware.ts | 9 ++- .../public/applications/endpoint/types.ts | 3 +- 5 files changed, 73 insertions(+), 43 deletions(-) diff --git a/x-pack/plugins/endpoint/common/generate_data.ts b/x-pack/plugins/endpoint/common/generate_data.ts index 9cc37c7f92afb..1d1d02fb45f0f 100644 --- a/x-pack/plugins/endpoint/common/generate_data.ts +++ b/x-pack/plugins/endpoint/common/generate_data.ts @@ -13,7 +13,7 @@ import { HostMetadata, OSFields, PolicyData, - PolicyResponse, + HostPolicyResponse, PolicyResponseActionStatus, } from './types'; import { factory as policyFactory } from './models/policy_config'; @@ -495,9 +495,12 @@ export class EndpointDocGenerator { }; } - generatePolicyResponse(): PolicyResponse { + /** + * Generates a Host Policy response message + */ + generatePolicyResponse(): HostPolicyResponse { return { - '@timestamp': new Date().getTime(), + '@timestamp': new Date().toISOString(), elastic: { agent: { id: 'c2a9093e-e289-4c0a-aa44-8c32a414fa7a', @@ -542,7 +545,7 @@ export class EndpointDocGenerator { configurations: { malware: { status: PolicyResponseActionStatus.success, - concerned_actions: ['download_model', 'workflow', 'a-custom-future-action'], + concerned_actions: ['download_model', 'workflow', 'a_custom_future_action'], }, events: { status: PolicyResponseActionStatus.success, @@ -557,7 +560,7 @@ export class EndpointDocGenerator { concerned_actions: [ 'detect_file_open_events', 'download_global_artifacts', - 'my-custom-value', + 'a_custom_future_action', ], }, }, @@ -574,6 +577,22 @@ export class EndpointDocGenerator { status: PolicyResponseActionStatus.success, message: 'the flow worked well', }, + a_custom_future_action: { + status: PolicyResponseActionStatus.success, + message: 'future message', + }, + configure_elasticsearch_connection: { + status: PolicyResponseActionStatus.success, + message: 'some message', + }, + detect_file_open_events: { + status: PolicyResponseActionStatus.success, + message: 'some message', + }, + download_global_artifacts: { + status: PolicyResponseActionStatus.success, + message: 'some message', + }, }, }, }, diff --git a/x-pack/plugins/endpoint/common/types.ts b/x-pack/plugins/endpoint/common/types.ts index 70c1ed10cda22..9f4ac0f754776 100644 --- a/x-pack/plugins/endpoint/common/types.ts +++ b/x-pack/plugins/endpoint/common/types.ts @@ -586,7 +586,7 @@ export enum PolicyResponseActionStatus { /** * The details of a given action */ -interface PolicyResponseActionDetails { +interface HostPolicyResponseActionDetails { status: PolicyResponseActionStatus; message: string; } @@ -594,42 +594,42 @@ interface PolicyResponseActionDetails { /** * A known list of possible Endpoint actions */ -interface PolicyResponseActions { - download_model: PolicyResponseActionDetails; - ingest_events_config: PolicyResponseActionDetails; - workflow: PolicyResponseActionDetails; - configure_elasticsearch_connection: PolicyResponseActionDetails; - configure_kernel: PolicyResponseActionDetails; - configure_logging: PolicyResponseActionDetails; - configure_malware: PolicyResponseActionDetails; - connect_kernel: PolicyResponseActionDetails; - detect_file_open_events: PolicyResponseActionDetails; - detect_file_write_events: PolicyResponseActionDetails; - detect_image_load_events: PolicyResponseActionDetails; - detect_process_events: PolicyResponseActionDetails; - download_global_artifacts: PolicyResponseActionDetails; - load_config: PolicyResponseActionDetails; - load_malware_model: PolicyResponseActionDetails; - read_elasticsearch_config: PolicyResponseActionDetails; - read_events_config: PolicyResponseActionDetails; - read_kernel_config: PolicyResponseActionDetails; - read_logging_config: PolicyResponseActionDetails; - read_malware_config: PolicyResponseActionDetails; +interface HostPolicyResponseActions { + download_model: HostPolicyResponseActionDetails; + ingest_events_config: HostPolicyResponseActionDetails; + workflow: HostPolicyResponseActionDetails; + configure_elasticsearch_connection: HostPolicyResponseActionDetails; + configure_kernel: HostPolicyResponseActionDetails; + configure_logging: HostPolicyResponseActionDetails; + configure_malware: HostPolicyResponseActionDetails; + connect_kernel: HostPolicyResponseActionDetails; + detect_file_open_events: HostPolicyResponseActionDetails; + detect_file_write_events: HostPolicyResponseActionDetails; + detect_image_load_events: HostPolicyResponseActionDetails; + detect_process_events: HostPolicyResponseActionDetails; + download_global_artifacts: HostPolicyResponseActionDetails; + load_config: HostPolicyResponseActionDetails; + load_malware_model: HostPolicyResponseActionDetails; + read_elasticsearch_config: HostPolicyResponseActionDetails; + read_events_config: HostPolicyResponseActionDetails; + read_kernel_config: HostPolicyResponseActionDetails; + read_logging_config: HostPolicyResponseActionDetails; + read_malware_config: HostPolicyResponseActionDetails; // The list of possible Actions will change rapidly, so the below entry will allow // them without us defining them here statically - [key: string]: PolicyResponseActionDetails; + [key: string]: HostPolicyResponseActionDetails; } -interface PolicyResponseConfigurationStatus { +interface HostPolicyResponseConfigurationStatus { status: PolicyResponseActionStatus; - concerned_actions: Array; + concerned_actions: Array; } /** * Information about the applying of a policy to a given host */ -export interface PolicyResponse { - '@timestamp': number; +export interface HostPolicyResponse { + '@timestamp': string; elastic: { agent: { id: string; @@ -655,14 +655,21 @@ export interface PolicyResponse { status: PolicyResponseActionStatus; response: { configurations: { - malware: PolicyResponseConfigurationStatus; - events: PolicyResponseConfigurationStatus; - logging: PolicyResponseConfigurationStatus; - streaming: PolicyResponseConfigurationStatus; + malware: HostPolicyResponseConfigurationStatus; + events: HostPolicyResponseConfigurationStatus; + logging: HostPolicyResponseConfigurationStatus; + streaming: HostPolicyResponseConfigurationStatus; }; - actions: Partial; + actions: Partial; }; }; }; }; } + +/** + * REST API response for retrieving a host's Policy Response status + */ +export interface GetHostPolicyResponse { + policy_response: HostPolicyResponse; +} diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/action.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/action.ts index c608f1cd41ff2..f4a55bab83a00 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/action.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/action.ts @@ -5,7 +5,7 @@ */ import { HostListPagination, ServerApiError } from '../../types'; -import { HostResultList, HostInfo } from '../../../../../common/types'; +import { HostResultList, HostInfo, GetHostPolicyResponse } from '../../../../../common/types'; interface ServerReturnedHostList { type: 'serverReturnedHostList'; @@ -24,7 +24,7 @@ interface ServerFailedToReturnHostDetails { interface ServerReturnedHostPolicyResponse { type: 'serverReturnedHostPolicyResponse'; - payload: { [key: string]: any }; // FIXME: replace with API response type once implemented + payload: GetHostPolicyResponse; } interface UserPaginatedHostList { type: 'userPaginatedHostList'; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/middleware.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/middleware.ts index 1559d32abd467..dba208f299726 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/middleware.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/middleware.ts @@ -7,6 +7,7 @@ import { ImmutableMiddlewareFactory } from '../../types'; import { pageIndex, pageSize, isOnHostPage, hasSelectedHost, uiQueryParams } from './selectors'; import { HostListState } from '../../types'; +import { HostPolicyResponse } from '../../../../../common/types'; export const hostMiddlewareFactory: ImmutableMiddlewareFactory = coreStart => { return ({ getState, dispatch }) => next => async action => { @@ -43,13 +44,15 @@ export const hostMiddlewareFactory: ImmutableMiddlewareFactory = dispatch({ type: 'serverReturnedHostPolicyResponse', payload: { - policy_response: { + policy_response: ({ endpoint: { policy: { - status: 'success', + applied: { + status: 'success', + }, }, }, - }, + } as unknown) as HostPolicyResponse, // Temporary until we get API }, }); } catch (error) { diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/types.ts b/x-pack/plugins/endpoint/public/applications/endpoint/types.ts index 2c3fffd502192..8ee7e3a7ba3a8 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/types.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/types.ts @@ -22,6 +22,7 @@ import { MalwareFields, UIPolicyConfig, PolicyData, + HostPolicyResponse, } from '../../../common/types'; import { EndpointPluginStartDependencies } from '../../plugin'; import { AppAction } from './store/action'; @@ -97,7 +98,7 @@ export interface HostListState { detailsError?: ServerApiError; details?: Immutable; /** Holds the Policy Response for the Host currently being displayed in the details */ - policyResponse?: { [key: string]: any }; // Will be Typed once we have an API for it + policyResponse?: HostPolicyResponse; location?: Immutable; } From 0e81f5d083e6ec9fe25aaabeef7515ca1bdba8dd Mon Sep 17 00:00:00 2001 From: Paul Tavares Date: Wed, 22 Apr 2020 09:24:31 -0400 Subject: [PATCH 06/12] Fix test case --- .../public/applications/endpoint/view/hosts/index.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/index.test.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/index.test.tsx index 88416b577ed0c..38fccec6abe44 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/index.test.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/index.test.tsx @@ -137,7 +137,7 @@ describe('when on the hosts page', () => { const renderResult = render(); const policyStatusLink = await renderResult.findByTestId('policyStatusValue'); expect(policyStatusLink).not.toBeNull(); - expect(policyStatusLink.textContent).toEqual('Successful'); + expect(policyStatusLink.textContent).toEqual('Success'); expect(policyStatusLink.getAttribute('href')).toEqual( '?selected_host=1&show=policy_response' ); From c7cc9107469f6f9df7e32584986a8e3f49b62129 Mon Sep 17 00:00:00 2001 From: Paul Tavares Date: Wed, 22 Apr 2020 10:40:16 -0400 Subject: [PATCH 07/12] Added test case list --- .../public/applications/endpoint/view/hosts/index.test.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/index.test.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/index.test.tsx index 38fccec6abe44..5df437aa08505 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/index.test.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/index.test.tsx @@ -152,6 +152,10 @@ describe('when on the hosts page', () => { const changedUrlAction = await userChangedUrlChecker; expect(changedUrlAction.payload.search).toEqual('?selected_host=1&show=policy_response'); }); + it.todo('should display Success overall policy status'); + it.todo('should display Warning overall policy status'); + it.todo('should display Failed overall policy status'); + it.todo('should display Unknown overall policy status'); it('should include the link to logs', async () => { const renderResult = render(); const linkToLogs = await renderResult.findByTestId('hostDetailsLinkToLogs'); @@ -174,7 +178,7 @@ describe('when on the hosts page', () => { expect(coreStart.application.navigateToApp.mock.calls).toHaveLength(1); }); }); - describe('when showing host Policy Response', () => { + describe('when showing host Policy Response panel', () => { let renderResult: ReturnType; beforeEach(async () => { renderResult = render(); From 36256431117d6c46b7177ecb1aa9e64963f5a368 Mon Sep 17 00:00:00 2001 From: Paul Tavares Date: Wed, 22 Apr 2020 13:36:51 -0400 Subject: [PATCH 08/12] Fix duplicate i18n id --- .../applications/endpoint/view/hosts/details/host_details.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/details/host_details.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/details/host_details.tsx index d7f11d51a9a8f..0d3181b1af411 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/details/host_details.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/details/host_details.tsx @@ -96,7 +96,7 @@ export const HostDetails = memo(({ details }: { details: HostMetadata }) => { onClick={policyStatusClickHandler} > From 874c848b347822d45717fee659fa8863b5b5803b Mon Sep 17 00:00:00 2001 From: Paul Tavares Date: Wed, 22 Apr 2020 14:56:27 -0400 Subject: [PATCH 09/12] stricter types for policy status -to- health color map --- .../endpoint/view/hosts/details/host_details.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/details/host_details.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/details/host_details.tsx index 0d3181b1af411..e876920b0ff1e 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/details/host_details.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/details/host_details.tsx @@ -16,7 +16,7 @@ import { import React, { memo, useMemo } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; -import { HostMetadata } from '../../../../../../common/types'; +import { HostMetadata, HostPolicyResponseActionStatus } from '../../../../../../common/types'; import { FormattedDateAndTime } from '../../formatted_date_time'; import { LinkToApp } from '../../components/link_to_app'; import { useHostListSelector, useHostLogsUrl } from '../hooks'; @@ -31,10 +31,12 @@ const HostIds = styled(EuiListGroupItem)` } `; -const POLICY_STATUS_TO_HEALTH_COLOR = Object.freeze({ +const POLICY_STATUS_TO_HEALTH_COLOR = Object.freeze< + { [key in keyof typeof HostPolicyResponseActionStatus]: string } +>({ success: 'success', warning: 'warning', - failed: 'danger', + failure: 'danger', }); export const HostDetails = memo(({ details }: { details: HostMetadata }) => { @@ -97,7 +99,7 @@ export const HostDetails = memo(({ details }: { details: HostMetadata }) => { > From a0d238992f8be437be82e4dad8e92853b7cf8f77 Mon Sep 17 00:00:00 2001 From: Paul Tavares Date: Wed, 22 Apr 2020 15:15:00 -0400 Subject: [PATCH 10/12] Added tests for host details overall policy status --- .../plugins/endpoint/common/generate_data.ts | 26 +++---- x-pack/plugins/endpoint/common/types.ts | 8 +- .../view/hosts/details/host_details.tsx | 5 +- .../endpoint/view/hosts/index.test.tsx | 74 +++++++++++++++++-- 4 files changed, 89 insertions(+), 24 deletions(-) diff --git a/x-pack/plugins/endpoint/common/generate_data.ts b/x-pack/plugins/endpoint/common/generate_data.ts index 1d1d02fb45f0f..4f5a6eaeb0a5e 100644 --- a/x-pack/plugins/endpoint/common/generate_data.ts +++ b/x-pack/plugins/endpoint/common/generate_data.ts @@ -14,7 +14,7 @@ import { OSFields, PolicyData, HostPolicyResponse, - PolicyResponseActionStatus, + HostPolicyResponseActionStatus, } from './types'; import { factory as policyFactory } from './models/policy_config'; @@ -540,23 +540,23 @@ export class EndpointDocGenerator { applied: { version: '1.0.0', id: '17d4b81d-9940-4b64-9de5-3e03ef1fb5cf', - status: PolicyResponseActionStatus.success, + status: HostPolicyResponseActionStatus.success, response: { configurations: { malware: { - status: PolicyResponseActionStatus.success, + status: HostPolicyResponseActionStatus.success, concerned_actions: ['download_model', 'workflow', 'a_custom_future_action'], }, events: { - status: PolicyResponseActionStatus.success, + status: HostPolicyResponseActionStatus.success, concerned_actions: ['ingest_events_config', 'workflow'], }, logging: { - status: PolicyResponseActionStatus.success, + status: HostPolicyResponseActionStatus.success, concerned_actions: ['configure_elasticsearch_connection'], }, streaming: { - status: PolicyResponseActionStatus.success, + status: HostPolicyResponseActionStatus.success, concerned_actions: [ 'detect_file_open_events', 'download_global_artifacts', @@ -566,31 +566,31 @@ export class EndpointDocGenerator { }, actions: { download_model: { - status: PolicyResponseActionStatus.success, + status: HostPolicyResponseActionStatus.success, message: 'model downloaded', }, ingest_events_config: { - status: PolicyResponseActionStatus.success, + status: HostPolicyResponseActionStatus.success, message: 'no action taken', }, workflow: { - status: PolicyResponseActionStatus.success, + status: HostPolicyResponseActionStatus.success, message: 'the flow worked well', }, a_custom_future_action: { - status: PolicyResponseActionStatus.success, + status: HostPolicyResponseActionStatus.success, message: 'future message', }, configure_elasticsearch_connection: { - status: PolicyResponseActionStatus.success, + status: HostPolicyResponseActionStatus.success, message: 'some message', }, detect_file_open_events: { - status: PolicyResponseActionStatus.success, + status: HostPolicyResponseActionStatus.success, message: 'some message', }, download_global_artifacts: { - status: PolicyResponseActionStatus.success, + status: HostPolicyResponseActionStatus.success, message: 'some message', }, }, diff --git a/x-pack/plugins/endpoint/common/types.ts b/x-pack/plugins/endpoint/common/types.ts index 9f4ac0f754776..4da4f9bc797ca 100644 --- a/x-pack/plugins/endpoint/common/types.ts +++ b/x-pack/plugins/endpoint/common/types.ts @@ -577,7 +577,7 @@ export type NewPolicyData = NewDatasource & { /** * the possible status for actions, configurations and overall Policy Response */ -export enum PolicyResponseActionStatus { +export enum HostPolicyResponseActionStatus { success = 'success', failure = 'failure', warning = 'warning', @@ -587,7 +587,7 @@ export enum PolicyResponseActionStatus { * The details of a given action */ interface HostPolicyResponseActionDetails { - status: PolicyResponseActionStatus; + status: HostPolicyResponseActionStatus; message: string; } @@ -621,7 +621,7 @@ interface HostPolicyResponseActions { } interface HostPolicyResponseConfigurationStatus { - status: PolicyResponseActionStatus; + status: HostPolicyResponseActionStatus; concerned_actions: Array; } @@ -652,7 +652,7 @@ export interface HostPolicyResponse { applied: { version: string; id: string; - status: PolicyResponseActionStatus; + status: HostPolicyResponseActionStatus; response: { configurations: { malware: HostPolicyResponseConfigurationStatus; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/details/host_details.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/details/host_details.tsx index e876920b0ff1e..ce4d078168611 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/details/host_details.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/details/host_details.tsx @@ -90,7 +90,10 @@ export const HostDetails = memo(({ details }: { details: HostMetadata }) => { defaultMessage: 'Policy Status', }), description: ( - + {/* eslint-disable-next-line @elastic/eui/href-or-on-click */} { + const docGenerator = new EndpointDocGenerator(); let render: () => ReturnType; let history: AppContextTestRender['history']; let store: AppContextTestRender['store']; @@ -91,6 +93,19 @@ describe('when on the hosts page', () => { describe('when there is a selected host in the url', () => { let hostDetails: HostInfo; + const dispatchServerReturnedHostPolicyResponse = ( + overallStatus: HostPolicyResponseActionStatus = HostPolicyResponseActionStatus.success + ) => { + const policyResponse = docGenerator.generatePolicyResponse(); + policyResponse.endpoint.policy.applied.status = overallStatus; + store.dispatch({ + type: 'serverReturnedHostPolicyResponse', + payload: { + policy_response: policyResponse, + }, + }); + }; + beforeEach(() => { const { host_status, @@ -137,7 +152,6 @@ describe('when on the hosts page', () => { const renderResult = render(); const policyStatusLink = await renderResult.findByTestId('policyStatusValue'); expect(policyStatusLink).not.toBeNull(); - expect(policyStatusLink.textContent).toEqual('Success'); expect(policyStatusLink.getAttribute('href')).toEqual( '?selected_host=1&show=policy_response' ); @@ -152,10 +166,58 @@ describe('when on the hosts page', () => { const changedUrlAction = await userChangedUrlChecker; expect(changedUrlAction.payload.search).toEqual('?selected_host=1&show=policy_response'); }); - it.todo('should display Success overall policy status'); - it.todo('should display Warning overall policy status'); - it.todo('should display Failed overall policy status'); - it.todo('should display Unknown overall policy status'); + it('should display Success overall policy status', async () => { + const renderResult = render(); + const policyStatusLink = await renderResult.findByTestId('policyStatusValue'); + expect(policyStatusLink.textContent).toEqual('Success'); + + const policyStatusHealth = await renderResult.findByTestId('policyStatusHealth'); + expect( + policyStatusHealth.querySelector('[data-euiicon-type][color="success"]') + ).not.toBeNull(); + }); + it('should display Warning overall policy status', async () => { + const renderResult = render(); + await middlewareSpy.waitForAction('serverReturnedHostPolicyResponse'); + reactTestingLibrary.act(() => { + dispatchServerReturnedHostPolicyResponse(HostPolicyResponseActionStatus.warning); + }); + const policyStatusLink = await renderResult.findByTestId('policyStatusValue'); + expect(policyStatusLink.textContent).toEqual('Warning'); + + const policyStatusHealth = await renderResult.findByTestId('policyStatusHealth'); + expect( + policyStatusHealth.querySelector('[data-euiicon-type][color="warning"]') + ).not.toBeNull(); + }); + it('should display Failed overall policy status', async () => { + const renderResult = render(); + await middlewareSpy.waitForAction('serverReturnedHostPolicyResponse'); + reactTestingLibrary.act(() => { + dispatchServerReturnedHostPolicyResponse(HostPolicyResponseActionStatus.failure); + }); + const policyStatusLink = await renderResult.findByTestId('policyStatusValue'); + expect(policyStatusLink.textContent).toEqual('Failed'); + + const policyStatusHealth = await renderResult.findByTestId('policyStatusHealth'); + expect( + policyStatusHealth.querySelector('[data-euiicon-type][color="danger"]') + ).not.toBeNull(); + }); + it('should display Unknown overall policy status', async () => { + const renderResult = render(); + await middlewareSpy.waitForAction('serverReturnedHostPolicyResponse'); + reactTestingLibrary.act(() => { + dispatchServerReturnedHostPolicyResponse('' as HostPolicyResponseActionStatus); + }); + const policyStatusLink = await renderResult.findByTestId('policyStatusValue'); + expect(policyStatusLink.textContent).toEqual('Unknown'); + + const policyStatusHealth = await renderResult.findByTestId('policyStatusHealth'); + expect( + policyStatusHealth.querySelector('[data-euiicon-type][color="subdued"]') + ).not.toBeNull(); + }); it('should include the link to logs', async () => { const renderResult = render(); const linkToLogs = await renderResult.findByTestId('hostDetailsLinkToLogs'); From bea9a0c72a26b558aec52e2d8c9da969070dddbe Mon Sep 17 00:00:00 2001 From: Paul Tavares Date: Fri, 24 Apr 2020 15:26:45 -0400 Subject: [PATCH 11/12] Fix type --- .../public/applications/endpoint/store/hosts/selectors.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/selectors.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/selectors.ts index 630cc9285d4e1..b0711baf9cdff 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/selectors.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/selectors.ts @@ -90,7 +90,7 @@ export const showView: (state: HostState) => 'policy_response' | 'details' = cre /** * Returns the Policy Response overall status */ -export const policyResponseStatus: (state: Immutable) => string = createSelector( +export const policyResponseStatus: (state: Immutable) => string = createSelector( state => state.policyResponse, policyResponse => { return (policyResponse && policyResponse?.endpoint?.policy?.applied?.status) || ''; From 92c1894643b27512ded682609bf9c78b8520dccc Mon Sep 17 00:00:00 2001 From: Paul Tavares Date: Mon, 27 Apr 2020 09:13:48 -0400 Subject: [PATCH 12/12] fix tests --- .../public/applications/endpoint/view/hosts/index.test.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/index.test.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/index.test.tsx index f99ed8e324339..499efb4f4b8ed 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/index.test.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/index.test.tsx @@ -170,6 +170,9 @@ describe('when on the hosts page', () => { }); it('should display Success overall policy status', async () => { const renderResult = render(); + reactTestingLibrary.act(() => { + dispatchServerReturnedHostPolicyResponse(HostPolicyResponseActionStatus.success); + }); const policyStatusLink = await renderResult.findByTestId('policyStatusValue'); expect(policyStatusLink.textContent).toEqual('Success'); @@ -180,7 +183,6 @@ describe('when on the hosts page', () => { }); it('should display Warning overall policy status', async () => { const renderResult = render(); - await middlewareSpy.waitForAction('serverReturnedHostPolicyResponse'); reactTestingLibrary.act(() => { dispatchServerReturnedHostPolicyResponse(HostPolicyResponseActionStatus.warning); }); @@ -194,7 +196,6 @@ describe('when on the hosts page', () => { }); it('should display Failed overall policy status', async () => { const renderResult = render(); - await middlewareSpy.waitForAction('serverReturnedHostPolicyResponse'); reactTestingLibrary.act(() => { dispatchServerReturnedHostPolicyResponse(HostPolicyResponseActionStatus.failure); }); @@ -208,7 +209,6 @@ describe('when on the hosts page', () => { }); it('should display Unknown overall policy status', async () => { const renderResult = render(); - await middlewareSpy.waitForAction('serverReturnedHostPolicyResponse'); reactTestingLibrary.act(() => { dispatchServerReturnedHostPolicyResponse('' as HostPolicyResponseActionStatus); });