diff --git a/x-pack/plugins/security_solution/public/common/components/links_to_docs/links_components.tsx b/x-pack/plugins/security_solution/public/common/components/links_to_docs/links_components.tsx index d5a3cfdefe64..82d4c5b5a3e2 100644 --- a/x-pack/plugins/security_solution/public/common/components/links_to_docs/links_components.tsx +++ b/x-pack/plugins/security_solution/public/common/components/links_to_docs/links_components.tsx @@ -22,3 +22,10 @@ export const DetectionsRequirementsLink = () => ( linkText={i18n.DETECTIONS_REQUIREMENTS_LINK_TEXT} /> ); + +export const MlJobCompatibilityLink = () => ( + +); diff --git a/x-pack/plugins/security_solution/public/common/components/links_to_docs/links_translations.ts b/x-pack/plugins/security_solution/public/common/components/links_to_docs/links_translations.ts index fc38899e7ff8..a495d95b3cf3 100644 --- a/x-pack/plugins/security_solution/public/common/components/links_to_docs/links_translations.ts +++ b/x-pack/plugins/security_solution/public/common/components/links_to_docs/links_translations.ts @@ -33,3 +33,11 @@ export const DETECTIONS_REQUIREMENTS_LINK_TEXT = i18n.translate( defaultMessage: 'Detections prerequisites and requirements', } ); + +export const ML_JOB_COMPATIBILITY_LINK_PATH = 'alerts-ui-monitor.html#ml-job-compatibility'; +export const ML_JOB_COMPATIBILITY_LINK_TEXT = i18n.translate( + 'xpack.securitySolution.documentationLinks.mlJobCompatibility.text', + { + defaultMessage: 'ML job compatibility', + } +); diff --git a/x-pack/plugins/security_solution/public/common/components/ml/hooks/use_installed_security_jobs.ts b/x-pack/plugins/security_solution/public/common/components/ml/hooks/use_installed_security_jobs.ts index 605fc6c09a94..7bdad2514c82 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/hooks/use_installed_security_jobs.ts +++ b/x-pack/plugins/security_solution/public/common/components/ml/hooks/use_installed_security_jobs.ts @@ -31,7 +31,7 @@ export interface UseInstalledSecurityJobsReturn { * necessary (running jobs, etc). * * NOTE: If you need to include jobs that are not currently installed, try the - * {@link useInstalledSecurityJobs} hook. + * {@link useSecurityJobs} hook. * */ export const useInstalledSecurityJobs = (): UseInstalledSecurityJobsReturn => { diff --git a/x-pack/plugins/security_solution/public/detections/components/callouts/ml_job_compatibility_callout/affected_job_ids.ts b/x-pack/plugins/security_solution/public/detections/components/callouts/ml_job_compatibility_callout/affected_job_ids.ts new file mode 100644 index 000000000000..8228a5c314c2 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/callouts/ml_job_compatibility_callout/affected_job_ids.ts @@ -0,0 +1,63 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +// These are the job IDs of ML jobs that are dependent on specific ECS data. If +// any of them is installed, we want to notify the user that they potentially +// have incompatibility between their beats, rules, and jobs. +// There are four modules of jobs that are affected. However, because the API +// that returns installed jobs does not include those jobs' modules, hardcoding +// the IDs from those modules (as found in e.g. +// x-pack/plugins/ml/server/models/data_recognizer/modules/security_windows/manifest.json) +// allows us to make this determination from a single API call. +export const affectedJobIds: string[] = [ + // security_linux module + 'v2_rare_process_by_host_linux_ecs', + 'v2_linux_rare_metadata_user', + 'v2_linux_rare_metadata_process', + 'v2_linux_anomalous_user_name_ecs', + 'v2_linux_anomalous_process_all_hosts_ecs', + 'v2_linux_anomalous_network_port_activity_ecs', + // security_windows module + 'v2_rare_process_by_host_windows_ecs', + 'v2_windows_anomalous_network_activity_ecs', + 'v2_windows_anomalous_path_activity_ecs', + 'v2_windows_anomalous_process_all_hosts_ecs', + 'v2_windows_anomalous_process_creation', + 'v2_windows_anomalous_user_name_ecs', + 'v2_windows_rare_metadata_process', + 'v2_windows_rare_metadata_user', + // siem_auditbeat module + 'rare_process_by_host_linux_ecs', + 'linux_anomalous_network_activity_ecs', + 'linux_anomalous_network_port_activity_ecs', + 'linux_anomalous_network_service', + 'linux_anomalous_network_url_activity_ecs', + 'linux_anomalous_process_all_hosts_ecs', + 'linux_anomalous_user_name_ecs', + 'linux_rare_metadata_process', + 'linux_rare_metadata_user', + 'linux_rare_user_compiler', + 'linux_rare_kernel_module_arguments', + 'linux_rare_sudo_user', + 'linux_system_user_discovery', + 'linux_system_information_discovery', + 'linux_system_process_discovery', + 'linux_network_connection_discovery', + 'linux_network_configuration_discovery', + // siem_winlogbeat module + 'rare_process_by_host_windows_ecs', + 'windows_anomalous_network_activity_ecs', + 'windows_anomalous_path_activity_ecs', + 'windows_anomalous_process_all_hosts_ecs', + 'windows_anomalous_process_creation', + 'windows_anomalous_script', + 'windows_anomalous_service', + 'windows_anomalous_user_name_ecs', + 'windows_rare_user_runas_event', + 'windows_rare_metadata_process', + 'windows_rare_metadata_user', +]; diff --git a/x-pack/plugins/security_solution/public/detections/components/callouts/ml_job_compatibility_callout/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/callouts/ml_job_compatibility_callout/index.test.tsx new file mode 100644 index 000000000000..23f6eea435d7 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/callouts/ml_job_compatibility_callout/index.test.tsx @@ -0,0 +1,69 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { mount } from 'enzyme'; +import React from 'react'; + +import { TestProviders } from '../../../../common/mock'; +import { useInstalledSecurityJobs } from '../../../../common/components/ml/hooks/use_installed_security_jobs'; +import { MlJobCompatibilityCallout } from './index'; + +jest.mock('../../../../common/components/ml/hooks/use_installed_security_jobs'); + +describe('MlJobCompatibilityCallout', () => { + it('renders when new affected jobs are installed', () => { + (useInstalledSecurityJobs as jest.Mock).mockReturnValue({ + loading: false, + jobs: [{ id: 'v2_linux_rare_metadata_process' }], + }); + const wrapper = mount( + + + + ); + expect(wrapper.exists('[data-test-subj="callout-ml-job-compatibility"]')).toEqual(true); + }); + + it('renders when old affected jobs are installed', () => { + (useInstalledSecurityJobs as jest.Mock).mockReturnValue({ + loading: false, + jobs: [{ id: 'linux_rare_metadata_process' }], + }); + const wrapper = mount( + + + + ); + expect(wrapper.exists('[data-test-subj="callout-ml-job-compatibility"]')).toEqual(true); + }); + + it('does not render if no affected jobs are installed', () => { + (useInstalledSecurityJobs as jest.Mock).mockReturnValue({ + loading: false, + jobs: [{ id: 'windows_rare_user_type10_remote_login' }], + }); + const wrapper = mount( + + + + ); + expect(wrapper.exists('[data-test-subj="callout-ml-job-compatibility"]')).toEqual(false); + }); + + it('does not render while jobs are loading', () => { + (useInstalledSecurityJobs as jest.Mock).mockReturnValue({ + loading: true, + jobs: [], + }); + const wrapper = mount( + + + + ); + expect(wrapper.exists('[data-test-subj="callout-ml-job-compatibility"]')).toEqual(false); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detections/components/callouts/ml_job_compatibility_callout/index.tsx b/x-pack/plugins/security_solution/public/detections/components/callouts/ml_job_compatibility_callout/index.tsx new file mode 100644 index 000000000000..ff266cfd859b --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/callouts/ml_job_compatibility_callout/index.tsx @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { memo } from 'react'; + +import { CallOutMessage, CallOutSwitcher } from '../../../../common/components/callouts'; +import { useInstalledSecurityJobs } from '../../../../common/components/ml/hooks/use_installed_security_jobs'; +import { affectedJobIds } from './affected_job_ids'; +import * as i18n from './translations'; + +const mlJobCompatibilityCalloutMessage: CallOutMessage = { + type: 'primary', + id: 'ml-job-compatibility', + title: i18n.ML_JOB_COMPATIBILITY_CALLOUT_TITLE, + description: , +}; + +const MlJobCompatibilityCalloutComponent = () => { + const { loading, jobs } = useInstalledSecurityJobs(); + const newJobsInstalled = jobs.some((job) => affectedJobIds.includes(job.id)); + + return ( + + ); +}; + +export const MlJobCompatibilityCallout = memo(MlJobCompatibilityCalloutComponent); diff --git a/x-pack/plugins/security_solution/public/detections/components/callouts/ml_job_compatibility_callout/translations.tsx b/x-pack/plugins/security_solution/public/detections/components/callouts/ml_job_compatibility_callout/translations.tsx new file mode 100644 index 000000000000..35ffdb6f7abf --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/callouts/ml_job_compatibility_callout/translations.tsx @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { MlJobCompatibilityLink } from '../../../../common/components/links_to_docs'; + +export const ML_JOB_COMPATIBILITY_CALLOUT_TITLE = i18n.translate( + 'xpack.securitySolution.detectionEngine.mlJobCompatibilityCallout.messageTitle', + { + defaultMessage: 'Your ML jobs may be incompatible with your data sources and/or ML rules', + } +); + +export const MlJobCompatibilityCalloutBody = () => ( + + +

+ ), + docs: ( +
    +
  • + +
  • +
+ ), + }} + /> +); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/index.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/index.tsx index 89cec1685101..57566986e47d 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/index.tsx @@ -36,6 +36,7 @@ import { SecurityPageName } from '../../../../app/types'; import { LinkButton } from '../../../../common/components/links'; import { useFormatUrl } from '../../../../common/components/link_to'; import { NeedAdminForUpdateRulesCallOut } from '../../../components/callouts/need_admin_for_update_callout'; +import { MlJobCompatibilityCallout } from '../../../components/callouts/ml_job_compatibility_callout'; type Func = () => Promise; @@ -161,6 +162,7 @@ const RulesPageComponent: React.FC = () => { <> + setShowValueListsModal(false)}