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

[Security Solution] Add threshold, machine_learning_job_id and anomaly_threshold editable fields #200323

Merged
merged 49 commits into from
Dec 18, 2024
Merged
Show file tree
Hide file tree
Changes from 47 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
11fe8b5
Add `machine_learning_job_id`
nikitaindik Nov 16, 2024
d57358b
Add `anomaly_threshold`
nikitaindik Nov 17, 2024
6f74bda
Add `threshold`
nikitaindik Nov 26, 2024
32b5477
Fix i18n
nikitaindik Nov 26, 2024
b6a9d92
Remove unused import
nikitaindik Nov 26, 2024
cc72aa9
Merge remote-tracking branch 'upstream/main' into machine-learning-fi…
nikitaindik Nov 27, 2024
9991cbd
Fix failing test
nikitaindik Nov 27, 2024
65e5266
Fix failing Jest tests
nikitaindik Nov 28, 2024
ef52eb1
Move `anomaly_threshold_edit.tsx` outside directory
nikitaindik Nov 28, 2024
2ef3c64
Simplify `componentProps` in `MachineLearningJobSelector`
nikitaindik Nov 28, 2024
7101058
Simplify validations
nikitaindik Nov 28, 2024
19be452
Use `??` instead of `||`
nikitaindik Nov 28, 2024
0e60504
Simplify `threshold` serialization and deserialization
nikitaindik Nov 28, 2024
5e7fd29
Add `assertUnreachable` to `ThresholdRuleFieldEdit`
nikitaindik Nov 28, 2024
ca03da3
Use `??` instead of `||` in `ThresholdAdapter`
nikitaindik Nov 28, 2024
f0b7db6
Remove redundant serializers and deserializers
nikitaindik Nov 28, 2024
27bc329
Refactor Machine Learning Job ID-related components
nikitaindik Nov 30, 2024
dc785d7
Merge remote-tracking branch 'upstream/main' into machine-learning-fi…
nikitaindik Nov 30, 2024
a9467b4
Remove `thresholdFields` from `StepDefineFieldsProps`
nikitaindik Nov 30, 2024
d161bf8
Fix failing ML HelpText test
nikitaindik Nov 30, 2024
a2b3c53
Make TS happy
nikitaindik Dec 1, 2024
3d56412
Use container queries instead of `EuiAutoSizer` for responsive layout
nikitaindik Dec 1, 2024
f4907b4
Merge remote-tracking branch 'upstream/main' into machine-learning-fi…
nikitaindik Dec 1, 2024
350ad19
Make validation work for ML job ID and move field config into edit co…
nikitaindik Dec 3, 2024
664b5a4
Merge remote-tracking branch 'upstream/main' into machine-learning-fi…
nikitaindik Dec 3, 2024
72b195a
Fix displaying long field names in Threshold input
nikitaindik Dec 3, 2024
068f879
Merge remote-tracking branch 'upstream/main' into machine-learning-fi…
nikitaindik Dec 4, 2024
9e231bb
Move "Create custom job" button margin outside of component
nikitaindik Dec 4, 2024
0933e39
Move aggregatable fields computation into `ThresholdEdit`
nikitaindik Dec 4, 2024
318b023
Merge branch 'main' into machine-learning-fields
nikitaindik Dec 5, 2024
26e947a
Merge branch 'main' into machine-learning-fields
nikitaindik Dec 6, 2024
a4e0c6e
Merge branch 'main' into machine-learning-fields
nikitaindik Dec 9, 2024
46daae5
Merge branch 'main' into machine-learning-fields
nikitaindik Dec 10, 2024
f30cb6d
Merge remote-tracking branch 'upstream/main' into machine-learning-fi…
nikitaindik Dec 11, 2024
c360516
Merge branch 'main' into machine-learning-fields
nikitaindik Dec 14, 2024
c175679
Merge branch 'main' into machine-learning-fields
nikitaindik Dec 16, 2024
0993cc0
Update imports to reflect latest structure changes
nikitaindik Dec 16, 2024
8a05118
Make sure alert suppression component updates when Threshold "Group b…
nikitaindik Dec 16, 2024
db6e424
Update `>=` operator styles
nikitaindik Dec 16, 2024
7fa5e9f
Merge remote-tracking branch 'upstream/main' into machine-learning-fi…
nikitaindik Dec 17, 2024
0e07340
Move files to match Sustainable Kibana Arch PR
nikitaindik Dec 17, 2024
349f448
Delete duplicated test file
nikitaindik Dec 17, 2024
0042063
Get rid of a custom margin in favour of EuiSpacer
nikitaindik Dec 17, 2024
1014b62
Remove unused import
nikitaindik Dec 17, 2024
8c786d5
Merge remote-tracking branch 'upstream/main' into machine-learning-fi…
nikitaindik Dec 18, 2024
dc59c29
Update styles
nikitaindik Dec 18, 2024
f1de5b6
Used fixed width for number inputs
nikitaindik Dec 18, 2024
468a083
Update spacing for ML job creation button
nikitaindik Dec 18, 2024
0975e4a
Merge remote-tracking branch 'upstream/main' into machine-learning-fi…
nikitaindik Dec 18, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -37757,7 +37757,6 @@
"xpack.securitySolution.detectionEngine.createRule.stepDefineRule.fieldThresholdCardinalityFieldLabel": "Compte",
"xpack.securitySolution.detectionEngine.createRule.stepDefineRule.fieldThresholdCardinalityValueFieldLabel": "Valeurs uniques",
"xpack.securitySolution.detectionEngine.createRule.stepDefineRule.fieldThresholdFieldCardinalityFieldHelpText": "Sélectionner un champ pour vérifier la cardinalité",
"xpack.securitySolution.detectionEngine.createRule.stepDefineRule.fieldThresholdLabel": "Seuil",
"xpack.securitySolution.detectionEngine.createRule.stepDefineRule.ga.enableThresholdSuppressionForFieldsLabel": "Supprimer les alertes par champs sélectionnés : {fieldsString}",
"xpack.securitySolution.detectionEngine.createRule.stepDefineRule.ga.enableThresholdSuppressionLabel": "Supprimer les alertes",
"xpack.securitySolution.detectionEngine.createRule.stepDefineRule.groupByDurationValueLabel": "Supprimer les alertes pour",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37615,7 +37615,6 @@
"xpack.securitySolution.detectionEngine.createRule.stepDefineRule.fieldThresholdCardinalityFieldLabel": "カウント",
"xpack.securitySolution.detectionEngine.createRule.stepDefineRule.fieldThresholdCardinalityValueFieldLabel": "一意の値",
"xpack.securitySolution.detectionEngine.createRule.stepDefineRule.fieldThresholdFieldCardinalityFieldHelpText": "カーディナリティを確認するフィールドを選択します",
"xpack.securitySolution.detectionEngine.createRule.stepDefineRule.fieldThresholdLabel": "しきい値",
"xpack.securitySolution.detectionEngine.createRule.stepDefineRule.ga.enableThresholdSuppressionForFieldsLabel": "選択したフィールドでアラートを非表示:{fieldsString}",
"xpack.securitySolution.detectionEngine.createRule.stepDefineRule.ga.enableThresholdSuppressionLabel": "アラートを非表示",
"xpack.securitySolution.detectionEngine.createRule.stepDefineRule.groupByDurationValueLabel": "アラートを非表示",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37043,7 +37043,6 @@
"xpack.securitySolution.detectionEngine.createRule.stepDefineRule.fieldThresholdCardinalityFieldLabel": "计数",
"xpack.securitySolution.detectionEngine.createRule.stepDefineRule.fieldThresholdCardinalityValueFieldLabel": "唯一值",
"xpack.securitySolution.detectionEngine.createRule.stepDefineRule.fieldThresholdFieldCardinalityFieldHelpText": "选择字段以检查基数",
"xpack.securitySolution.detectionEngine.createRule.stepDefineRule.fieldThresholdLabel": "阈值",
"xpack.securitySolution.detectionEngine.createRule.stepDefineRule.ga.enableThresholdSuppressionForFieldsLabel": "对选定字段阻止告警:{fieldsString}",
"xpack.securitySolution.detectionEngine.createRule.stepDefineRule.ga.enableThresholdSuppressionLabel": "阻止告警",
"xpack.securitySolution.detectionEngine.createRule.stepDefineRule.groupByDurationValueLabel": "阻止以下项的告警",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
* 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 type { MachineLearningJobId } from '../../../common/api/detection_engine';

export function normalizeMachineLearningJobId(jobId: MachineLearningJobId): string[] {
return typeof jobId === 'string' ? [jobId] : jobId;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* 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 { UseField } from '../../../../shared_imports';
import { AnomalyThresholdSlider } from '../../../rule_creation_ui/components/anomaly_threshold_slider';
import * as i18n from './translations';

const componentProps = {
describedByIds: ['anomalyThreshold'],
};

interface AnomalyThresholdEditProps {
path: string;
}

export function AnomalyThresholdEdit({ path }: AnomalyThresholdEditProps): JSX.Element {
return (
<UseField
path={path}
config={ANOMALY_THRESHOLD_FIELD_CONFIG}
component={AnomalyThresholdSlider}
componentProps={componentProps}
/>
);
}

const ANOMALY_THRESHOLD_FIELD_CONFIG = {
label: i18n.ANOMALY_THRESHOLD_LABEL,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
* 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.
*/

export { AnomalyThresholdEdit } from './anomaly_threshold_edit';
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* 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 { i18n } from '@kbn/i18n';

export const ANOMALY_THRESHOLD_LABEL = i18n.translate(
'xpack.securitySolution.detectionEngine.createRule.stepDefineRule.fieldAnomalyThresholdLabel',
{
defaultMessage: 'Anomaly score threshold',
}
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* 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 { EuiButton } from '@elastic/eui';
import { useKibana } from '../../../../common/lib/kibana';
import * as i18n from './translations';

export function CreateCustomMlJobButton(): JSX.Element {
const { navigateToApp } = useKibana().services.application;

return (
<EuiButton
iconType="popout"
iconSide="right"
onClick={() => navigateToApp('ml', { openInNewTab: true })}
>
{i18n.CREATE_CUSTOM_JOB_BUTTON_TITLE}
</EuiButton>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* 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 { i18n } from '@kbn/i18n';

export const CREATE_CUSTOM_JOB_BUTTON_TITLE = i18n.translate(
'xpack.securitySolution.detectionEngine.mlSelectJob.createCustomJobButtonTitle',
{
defaultMessage: 'Create custom job',
}
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
* 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.
*/

export { MachineLearningJobIdEdit } from './machine_learning_job_id_edit';
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* 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, { useMemo } from 'react';
import { UseField, fieldValidators } from '../../../../shared_imports';
import { MlJobSelect } from '../ml_job_select';
import { useSecurityJobs } from '../../../../common/components/ml_popover/hooks/use_security_jobs';
import * as i18n from './translations';

interface MachineLearningJobIdEditProps {
path: string;
shouldShowHelpText?: boolean;
}

export function MachineLearningJobIdEdit({
path,
shouldShowHelpText,
}: MachineLearningJobIdEditProps): JSX.Element {
const { loading, jobs } = useSecurityJobs();

const componentProps = useMemo(
() => ({
jobs,
loading,
shouldShowHelpText,
}),
[jobs, loading, shouldShowHelpText]
);

return (
<UseField
path={path}
config={MACHINE_LEARNING_JOB_ID_FIELD_CONFIG}
component={MlJobSelect}
componentProps={componentProps}
/>
);
}

const MACHINE_LEARNING_JOB_ID_FIELD_CONFIG = {
label: i18n.MACHINE_LEARNING_JOB_ID_LABEL,
validations: [
{
validator: fieldValidators.emptyField(
i18n.MACHINE_LEARNING_JOB_ID_EMPTY_FIELD_VALIDATION_ERROR
),
},
],
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* 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 { i18n } from '@kbn/i18n';

export const MACHINE_LEARNING_JOB_ID_LABEL = i18n.translate(
'xpack.securitySolution.detectionEngine.createRule.stepDefineRule.fieldMachineLearningJobIdLabel',
{
defaultMessage: 'Machine Learning job',
}
);

export const MACHINE_LEARNING_JOB_ID_EMPTY_FIELD_VALIDATION_ERROR = i18n.translate(
'xpack.securitySolution.detectionEngine.createRule.stepDefineRule.machineLearningJobIdRequired',
{
defaultMessage: 'A Machine Learning job is required.',
}
);
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,46 @@
import React from 'react';
import { shallow } from 'enzyme';
import { HelpText } from './help_text';
import type { SecurityJob } from '../../../../common/components/ml_popover/types';

jest.mock('../../../../common/lib/kibana', () => {
return {
useKibana: jest.fn().mockReturnValue({
services: {
application: {
getUrlForApp: () => '/app/ml',
},
},
}),
};
});

describe('MlJobSelect help text', () => {
it('does not show warning if all jobs are running', () => {
const wrapper = shallow(<HelpText href={'https://test.com'} notRunningJobIds={[]} />);
const jobs = [
{
id: 'test-id',
jobState: 'opened',
datafeedState: 'opened',
},
] as SecurityJob[];
const selectedJobIds = ['test-id'];

const wrapper = shallow(<HelpText jobs={jobs} selectedJobIds={selectedJobIds} />);
expect(wrapper.find('[data-test-subj="ml-warning-not-running-jobs"]')).toHaveLength(0);
});

it('shows warning if there are jobs not running', () => {
const wrapper = shallow(<HelpText href={'https://test.com'} notRunningJobIds={['id']} />);
const jobs = [
{
id: 'test-id',
jobState: 'closed',
datafeedState: 'stopped',
},
] as SecurityJob[];
const selectedJobIds = ['test-id'];

const wrapper = shallow(<HelpText jobs={jobs} selectedJobIds={selectedJobIds} />);
expect(wrapper.find('[data-test-subj="ml-warning-not-running-jobs"]')).toHaveLength(1);
});
});
Loading