diff --git a/x-pack/plugins/endpoint/common/generate_data.ts b/x-pack/plugins/endpoint/common/generate_data.ts index ff8add42a5085..281611ce4eeb6 100644 --- a/x-pack/plugins/endpoint/common/generate_data.ts +++ b/x-pack/plugins/endpoint/common/generate_data.ts @@ -13,7 +13,6 @@ import { HostMetadata, HostOS, HostPolicyResponse, - HostPolicyResponseActions, HostPolicyResponseActionStatus, PolicyData, } from './types'; @@ -558,88 +557,108 @@ export class EndpointDocGenerator { endpoint: { policy: { applied: { - actions: { - configure_elasticsearch_connection: { - message: 'elasticsearch communications configured successfully', + actions: [ + { + name: 'configure_elasticsearch_connection', + message: 'elasticsearch comes configured successfully', status: HostPolicyResponseActionStatus.success, }, - configure_kernel: { + { + name: 'configure_kernel', message: 'Failed to configure kernel', status: HostPolicyResponseActionStatus.failure, }, - configure_logging: { + { + name: 'configure_logging', message: 'Successfully configured logging', status: HostPolicyResponseActionStatus.success, }, - configure_malware: { + { + name: 'configure_malware', message: 'Unexpected error configuring malware', status: HostPolicyResponseActionStatus.failure, }, - connect_kernel: { + { + name: 'connect_kernel', message: 'Successfully initialized minifilter', status: HostPolicyResponseActionStatus.success, }, - detect_file_open_events: { + { + name: 'detect_file_open_events', message: 'Successfully stopped file open event reporting', status: HostPolicyResponseActionStatus.success, }, - detect_file_write_events: { + { + name: 'detect_file_write_events', message: 'Failed to stop file write event reporting', status: HostPolicyResponseActionStatus.success, }, - detect_image_load_events: { + { + name: 'detect_image_load_events', message: 'Successfuly started image load event reporting', status: HostPolicyResponseActionStatus.success, }, - detect_process_events: { + { + name: 'detect_process_events', message: 'Successfully started process event reporting', status: HostPolicyResponseActionStatus.success, }, - download_global_artifacts: { + { + name: 'download_global_artifacts', message: 'Failed to download EXE model', status: HostPolicyResponseActionStatus.success, }, - load_config: { + { + name: 'load_config', message: 'successfully parsed configuration', status: HostPolicyResponseActionStatus.success, }, - load_malware_model: { + { + name: 'load_malware_mode', message: 'Error deserializing EXE model; no valid malware model installed', status: HostPolicyResponseActionStatus.success, }, - read_elasticsearch_config: { + { + name: 'read_elasticsearch_config', message: 'Successfully read Elasticsearch configuration', status: HostPolicyResponseActionStatus.success, }, - read_events_config: { + { + name: 'read_events_config', message: 'Successfully read events configuration', status: HostPolicyResponseActionStatus.success, }, - read_kernel_config: { + { + name: 'read_kernel_config', message: 'Succesfully read kernel configuration', status: HostPolicyResponseActionStatus.success, }, - read_logging_config: { + { + name: 'read_logging_config', message: 'field (logging.debugview) not found in config', status: HostPolicyResponseActionStatus.success, }, - read_malware_config: { + { + name: 'read_malware_config', message: 'Successfully read malware detect configuration', status: HostPolicyResponseActionStatus.success, }, - workflow: { + { + name: 'workflow', message: 'Failed to apply a portion of the configuration (kernel)', status: HostPolicyResponseActionStatus.success, }, - download_model: { + { + name: 'download_model', message: 'Failed to apply a portion of the configuration (kernel)', status: HostPolicyResponseActionStatus.success, }, - ingest_events_config: { + { + name: 'ingest_events_config', message: 'Failed to apply a portion of the configuration (kernel)', status: HostPolicyResponseActionStatus.success, }, - }, + ], id: this.commonInfo.endpoint.policy.id, policy: { id: this.commonInfo.endpoint.policy.id, @@ -648,23 +667,43 @@ export class EndpointDocGenerator { response: { configurations: { events: { - concerned_actions: ['download_model'], + concerned_actions: this.randomHostPolicyResponseActionNames(), status: this.randomHostPolicyResponseActionStatus(), }, logging: { - concerned_actions: this.randomHostPolicyResponseActions(), + concerned_actions: this.randomHostPolicyResponseActionNames(), status: this.randomHostPolicyResponseActionStatus(), }, malware: { - concerned_actions: this.randomHostPolicyResponseActions(), + concerned_actions: this.randomHostPolicyResponseActionNames(), status: this.randomHostPolicyResponseActionStatus(), }, streaming: { - concerned_actions: this.randomHostPolicyResponseActions(), + concerned_actions: this.randomHostPolicyResponseActionNames(), status: this.randomHostPolicyResponseActionStatus(), }, }, }, + artifacts: { + global: { + version: '1.4.0', + identifiers: [ + { + name: 'endpointpe-model', + sha256: 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', + }, + ], + }, + user: { + version: '1.4.0', + identifiers: [ + { + name: 'user-model', + sha256: 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', + }, + ], + }, + }, status: this.randomHostPolicyResponseActionStatus(), version: policyVersion, }, @@ -673,7 +712,12 @@ export class EndpointDocGenerator { event: { created: ts, id: this.seededUUIDv4(), - kind: 'policy_response', + kind: 'state', + category: 'host', + type: 'change', + module: 'endpoint', + action: 'endpoint_policy_response', + dataset: 'endpoint.policy', }, }; } @@ -722,7 +766,7 @@ export class EndpointDocGenerator { return uuid.v4({ random: [...this.randomNGenerator(255, 16)] }); } - private randomHostPolicyResponseActions(): Array { + private randomHostPolicyResponseActionNames(): string[] { return this.randomArray(this.randomN(8), () => this.randomChoice([ 'load_config', diff --git a/x-pack/plugins/endpoint/common/types.ts b/x-pack/plugins/endpoint/common/types.ts index b39b2e89ee150..3d096eab5c4f2 100644 --- a/x-pack/plugins/endpoint/common/types.ts +++ b/x-pack/plugins/endpoint/common/types.ts @@ -611,47 +611,53 @@ export enum HostPolicyResponseActionStatus { } /** - * The details of a given action + * The name of actions that can be applied during the processing of a policy */ -export interface HostPolicyResponseActionDetails { - status: HostPolicyResponseActionStatus; - message: string; -} +type HostPolicyActionName = + | 'download_model' + | 'ingest_events_config' + | 'workflow' + | 'configure_elasticsearch_connection' + | 'configure_kernel' + | 'configure_logging' + | 'configure_malware' + | 'connect_kernel' + | 'detect_file_open_events' + | 'detect_file_write_events' + | 'detect_image_load_events' + | 'detect_process_events' + | 'download_global_artifacts' + | 'load_config' + | 'load_malware_model' + | 'read_elasticsearch_config' + | 'read_events_config' + | 'read_kernel_config' + | 'read_logging_config' + | 'read_malware_config' + | string; /** - * A known list of possible Endpoint actions + * Host Policy Response Applied Action */ -export 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; +export interface HostPolicyResponseAppliedAction { + name: HostPolicyActionName; + status: HostPolicyResponseActionStatus; + message: string; } -/** - * policy configurations returned by the endpoint in response to a user applying a policy - */ export type HostPolicyResponseConfiguration = HostPolicyResponse['endpoint']['policy']['applied']['response']['configurations']; interface HostPolicyResponseConfigurationStatus { status: HostPolicyResponseActionStatus; - concerned_actions: Array; + concerned_actions: HostPolicyActionName[]; +} + +/** + * Host Policy Response Applied Artifact + */ +interface HostPolicyResponseAppliedArtifact { + name: string; + sha256: string; } /** @@ -674,6 +680,11 @@ export interface HostPolicyResponse { created: number; kind: string; id: string; + category: string; + type: string; + module: string; + action: string; + dataset: string; }; agent: { version: string; @@ -685,7 +696,7 @@ export interface HostPolicyResponse { version: string; id: string; status: HostPolicyResponseActionStatus; - actions: Partial; + actions: HostPolicyResponseAppliedAction[]; policy: { id: string; version: string; @@ -698,6 +709,16 @@ export interface HostPolicyResponse { streaming: HostPolicyResponseConfigurationStatus; }; }; + artifacts: { + global: { + version: string; + identifiers: HostPolicyResponseAppliedArtifact[]; + }; + user: { + version: string; + identifiers: HostPolicyResponseAppliedArtifact[]; + }; + }; }; }; }; 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 a5378a02ed6fb..7c76de98fe0f4 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 @@ -80,16 +80,18 @@ export const hostMiddlewareFactory: ImmutableMiddlewareFactory = core version: '1.0.0', status: HostPolicyResponseActionStatus.success, id: '17d4b81d-9940-4b64-9de5-3e03ef1fb5cf', - actions: { - download_model: { + actions: [ + { + name: 'download_model', status: 'success', message: 'Model downloaded', }, - ingest_events_config: { + { + name: 'ingest_events_config', status: 'failure', message: 'No action taken', }, - }, + ], response: { configurations: { malware: { 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 e16d4ff5d18c2..6d7b5c81e755c 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 @@ -7,7 +7,7 @@ import querystring from 'querystring'; import { createSelector } from 'reselect'; import { Immutable, - HostPolicyResponseActions, + HostPolicyResponseAppliedAction, HostPolicyResponseConfiguration, HostPolicyResponseActionStatus, } from '../../../../../common/types'; @@ -62,7 +62,8 @@ export const policyResponseFailedOrWarningActionCount: ( Object.entries(applied.response.configurations).map(([key, val]) => { let count = 0; for (const action of val.concerned_actions) { - const actionStatus = applied.actions[action]?.status; + const actionStatus = applied.actions.find(policyActions => policyActions.name === action) + ?.status; if ( actionStatus === HostPolicyResponseActionStatus.failure || actionStatus === HostPolicyResponseActionStatus.warning @@ -81,7 +82,7 @@ export const policyResponseFailedOrWarningActionCount: ( */ export const policyResponseActions: ( state: Immutable -) => undefined | Partial = createSelector( +) => undefined | Immutable = createSelector( detailsPolicyAppliedResponse, applied => { return applied?.actions; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/details/index.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/details/index.tsx index 017ce9a66f8c5..b29f415d4f1ed 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/details/index.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/details/index.tsx @@ -106,7 +106,7 @@ const PolicyResponseFlyoutPanel = memo<{ }>(({ hostMeta }) => { const { show, ...queryParams } = useHostSelector(uiQueryParams); const responseConfig = useHostSelector(policyResponseConfigurations); - const responseActionStatus = useHostSelector(policyResponseActions); + const responseActions = useHostSelector(policyResponseActions); const responseAttentionCount = useHostSelector(policyResponseFailedOrWarningActionCount); const detailsUri = useMemo( () => @@ -142,10 +142,10 @@ const PolicyResponseFlyoutPanel = memo<{ /> - {responseConfig !== undefined && responseActionStatus !== undefined ? ( + {responseConfig !== undefined && responseActions !== undefined ? ( ) : ( diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/details/policy_response.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/details/policy_response.tsx index 8714141364e7d..cb561863f26f8 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/details/policy_response.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/details/policy_response.tsx @@ -9,7 +9,7 @@ import { EuiAccordion, EuiNotificationBadge, EuiHealth } from '@elastic/eui'; import { EuiText } from '@elastic/eui'; import { htmlIdGenerator } from '@elastic/eui'; import { - HostPolicyResponseActions, + HostPolicyResponseAppliedAction, HostPolicyResponseConfiguration, Immutable, } from '../../../../../../common/types'; @@ -48,15 +48,15 @@ const PolicyResponseConfigAccordion = styled(EuiAccordion)` const ResponseActions = memo( ({ actions, - actionStatus, + responseActions, }: { - actions: Immutable>; - actionStatus: Partial; + actions: Immutable; + responseActions: Immutable; }) => { return ( <> {actions.map((action, index) => { - const statuses = actionStatus[action]; + const statuses = responseActions.find(responseAction => responseAction.name === action); if (statuses === undefined) { return undefined; } @@ -99,11 +99,11 @@ const ResponseActions = memo( export const PolicyResponse = memo( ({ responseConfig, - responseActionStatus, + responseActions, responseAttentionCount, }: { responseConfig: Immutable; - responseActionStatus: Partial; + responseActions: Immutable; responseAttentionCount: Map; }) => { return ( @@ -133,10 +133,7 @@ export const PolicyResponse = memo( ) } > - + ); })} 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 aaeff935b32b4..d71749ad05ed3 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 @@ -128,12 +128,23 @@ describe('when on the hosts page', () => { const policyResponse = docGenerator.generatePolicyResponse(); policyResponse.endpoint.policy.applied.status = overallStatus; policyResponse.endpoint.policy.applied.response.configurations.malware.status = overallStatus; - policyResponse.endpoint.policy.applied.actions.download_model!.status = overallStatus; + let downloadModelAction = policyResponse.endpoint.policy.applied.actions.find( + action => action.name === 'download_model' + ); + + if (!downloadModelAction) { + downloadModelAction = { + name: 'download_model', + message: 'Failed to apply a portion of the configuration (kernel)', + status: overallStatus, + }; + policyResponse.endpoint.policy.applied.actions.push(downloadModelAction); + } if ( overallStatus === HostPolicyResponseActionStatus.failure || overallStatus === HostPolicyResponseActionStatus.warning ) { - policyResponse.endpoint.policy.applied.actions.download_model!.message = 'no action taken'; + downloadModelAction.message = 'no action taken'; } store.dispatch({ type: 'serverReturnedHostPolicyResponse', diff --git a/x-pack/plugins/endpoint/scripts/policy_mapping.json b/x-pack/plugins/endpoint/scripts/policy_mapping.json index 1fdd5d140e0ba..b879ba180eba8 100644 --- a/x-pack/plugins/endpoint/scripts/policy_mapping.json +++ b/x-pack/plugins/endpoint/scripts/policy_mapping.json @@ -35,234 +35,66 @@ "properties": { "actions": { "properties": { - "configure_elasticsearch_connection": { - "properties": { - "message": { - "ignore_above": 1024, - "type": "keyword" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "object" - }, - "configure_kernel": { - "properties": { - "message": { - "ignore_above": 1024, - "type": "keyword" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "object" - }, - "configure_logging": { - "properties": { - "message": { - "ignore_above": 1024, - "type": "keyword" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "object" - }, - "configure_malware": { - "properties": { - "message": { - "ignore_above": 1024, - "type": "keyword" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "object" - }, - "connect_kernel": { - "properties": { - "message": { - "ignore_above": 1024, - "type": "keyword" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "object" - }, - "detect_file_open_events": { - "properties": { - "message": { - "ignore_above": 1024, - "type": "keyword" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "object" - }, - "detect_file_write_events": { - "properties": { - "message": { - "ignore_above": 1024, - "type": "keyword" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "object" - }, - "detect_image_load_events": { - "properties": { - "message": { - "ignore_above": 1024, - "type": "keyword" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "object" - }, - "detect_process_events": { - "properties": { - "message": { - "ignore_above": 1024, - "type": "keyword" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "object" - }, - "download_global_artifacts": { - "properties": { - "message": { - "ignore_above": 1024, - "type": "keyword" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "object" - }, - "load_config": { - "properties": { - "message": { - "ignore_above": 1024, - "type": "keyword" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "object" - }, - "load_malware_model": { - "properties": { - "message": { - "ignore_above": 1024, - "type": "keyword" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "object" - }, - "read_elasticsearch_config": { - "properties": { - "message": { - "ignore_above": 1024, - "type": "keyword" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "object" - }, - "read_events_config": { - "properties": { - "message": { - "ignore_above": 1024, - "type": "keyword" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "object" + "message": { + "ignore_above": 1024, + "type": "keyword" }, - "read_kernel_config": { - "properties": { - "message": { - "ignore_above": 1024, - "type": "keyword" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "type": "object" + "name": { + "ignore_above": 1024, + "type": "keyword" }, - "read_logging_config": { + "status": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "artifacts": { + "properties": { + "global": { "properties": { - "message": { - "ignore_above": 1024, - "type": "keyword" + "identifiers": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" }, - "status": { + "version": { "ignore_above": 1024, "type": "keyword" } }, "type": "object" }, - "read_malware_config": { + "user": { "properties": { - "message": { - "ignore_above": 1024, - "type": "keyword" + "identifiers": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" }, - "status": { + "version": { "ignore_above": 1024, "type": "keyword" } }, "type": "object" - }, - "workflow": { - "properties": { - "status": { - "ignore_above": 1024, - "type": "keyword" - } - } } }, "type": "object" @@ -362,9 +194,21 @@ }, "event": { "properties": { + "action": { + "ignore_above": 1024, + "type": "keyword" + }, + "category": { + "ignore_above": 1024, + "type": "keyword" + }, "created": { "type": "date" }, + "dataset": { + "ignore_above": 1024, + "type": "keyword" + }, "id": { "ignore_above": 1024, "type": "keyword" @@ -372,6 +216,14 @@ "kind": { "ignore_above": 1024, "type": "keyword" + }, + "module": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" } } }, @@ -382,6 +234,10 @@ "type": "keyword" } } + }, + "message": { + "norms": false, + "type": "text" } } },