From 0452b4f270af7389f08aae3a8be11ee144826d25 Mon Sep 17 00:00:00 2001 From: Amardeepsingh Siglani Date: Thu, 19 Oct 2023 02:58:30 -0700 Subject: [PATCH 1/9] added threat intel feed support for detector creation Signed-off-by: Amardeepsingh Siglani --- cypress/integration/1_detectors.spec.js | 2 +- .../AlertCondition/AlertConditionPanel.tsx | 212 +- .../AlertConditionPanel.test.tsx.snap | 266 ++- .../containers/ConfigureAlerts.tsx | 18 +- .../DetectionRules/DetectionRules.tsx | 5 +- .../DetectorDataSource/DetectorDataSource.tsx | 9 +- .../DetectorBasicDetailsForm.tsx | 10 +- .../DetectorSchedule/DetectorSchedule.tsx | 11 +- .../components/DetectorType/DetectorType.tsx | 25 +- .../ThreatIntelligence/ThreatIntelligence.tsx | 38 + .../containers/DefineDetector.tsx | 48 +- .../containers/CreateDetector.tsx | 20 +- .../UpdateBasicDetails/UpdateBasicDetails.tsx | 77 +- .../UpdateDetectorBasicDetails.test.tsx.snap | 1915 ++++++++--------- public/pages/Rules/utils/constants.ts | 8 +- public/store/LogTypeStore.ts | 3 +- public/utils/constants.ts | 1 + types/Detector.ts | 1 + 18 files changed, 1371 insertions(+), 1298 deletions(-) create mode 100644 public/pages/CreateDetector/components/DefineDetector/components/ThreatIntelligence/ThreatIntelligence.tsx diff --git a/cypress/integration/1_detectors.spec.js b/cypress/integration/1_detectors.spec.js index 5d4439ecf..08785f525 100644 --- a/cypress/integration/1_detectors.spec.js +++ b/cypress/integration/1_detectors.spec.js @@ -37,7 +37,7 @@ const dataSourceLabel = 'Select or input source indexes or index patterns'; const getDataSourceField = () => cy.getFieldByLabel(dataSourceLabel); -const logTypeLabel = 'Select a log type you would like to detect'; +const logTypeLabel = 'Log type'; const getLogTypeField = () => cy.getFieldByLabel(logTypeLabel); diff --git a/public/pages/CreateDetector/components/ConfigureAlerts/components/AlertCondition/AlertConditionPanel.tsx b/public/pages/CreateDetector/components/ConfigureAlerts/components/AlertCondition/AlertConditionPanel.tsx index 9025bd3e9..95e53e1af 100644 --- a/public/pages/CreateDetector/components/ConfigureAlerts/components/AlertCondition/AlertConditionPanel.tsx +++ b/public/pages/CreateDetector/components/ConfigureAlerts/components/AlertCondition/AlertConditionPanel.tsx @@ -8,6 +8,7 @@ import { RouteComponentProps } from 'react-router-dom'; import { EuiAccordion, EuiButton, + EuiButtonGroup, EuiComboBox, EuiComboBoxOptionOption, EuiFieldText, @@ -17,6 +18,7 @@ import { EuiSpacer, EuiText, EuiTextArea, + EuiTitle, } from '@elastic/eui'; import { AlertCondition } from '../../../../../../../models/interfaces'; import { @@ -50,12 +52,24 @@ interface AlertConditionPanelState { nameIsInvalid: boolean; previewToggle: boolean; selectedNames: EuiComboBoxOptionOption[]; + selectedDetectionTypeId: string; } export default class AlertConditionPanel extends Component< AlertConditionPanelProps, AlertConditionPanelState > { + private detectionTypeGroupBtns = [ + { + id: 'detection-rules', + label: 'Detection rules', + }, + { + id: 'threat-intelligence', + label: 'Threat intelligence', + }, + ]; + constructor(props: AlertConditionPanelProps) { super(props); this.state = { @@ -63,6 +77,7 @@ export default class AlertConditionPanel extends Component< nameIsInvalid: false, previewToggle: false, selectedNames: [], + selectedDetectionTypeId: this.detectionTypeGroupBtns[0].id, }; } @@ -253,6 +268,7 @@ export default class AlertConditionPanel extends Component< const { alertCondition = getEmptyAlertCondition(), allNotificationChannels, + detector: { threat_intel_enabled }, indexNum, loadingNotifications, refreshNotificationChannels, @@ -315,97 +331,123 @@ export default class AlertConditionPanel extends Component< return (
- - Trigger details and condition - - {triggerDetailsSubheading} - -
+ +

Trigger name

+ } + isInvalid={nameFieldTouched && nameIsInvalid} + error={getNameErrorMessage(name, nameIsInvalid, nameFieldTouched)} > - -

Trigger name

- - } - isInvalid={nameFieldTouched && nameIsInvalid} - error={getNameErrorMessage(name, nameIsInvalid, nameFieldTouched)} - > - -
- - - -

If a detection rule matches

-
- - - -

Rule names

- - } - > - -
- + +
+ - -

Rule Severities

- - } - > - + {threat_intel_enabled ? ( + { + this.setState({ selectedDetectionTypeId: id }); + }} + isFullWidth /> -
- - - -

Tags

- + ) : ( + +

Detection rules

+
+ )} +
+ + {threat_intel_enabled && this.state.selectedDetectionTypeId === 'threat-intelligence' ? ( + <> + +

Trigger condition

+
+ +

+ An alert will be generated when any match is found by the threat intelligence feed. +

+
+ + ) : ( + + Trigger condition + + {triggerDetailsSubheading} + + } > - - - + +

Rule names

+ + } + > + +
+ + + +

Rule Severities

+ + } + > + +
+ + + +

Tags

+ + } + > + +
+ + )} diff --git a/public/pages/CreateDetector/components/ConfigureAlerts/components/AlertCondition/__snapshots__/AlertConditionPanel.test.tsx.snap b/public/pages/CreateDetector/components/ConfigureAlerts/components/AlertCondition/__snapshots__/AlertConditionPanel.test.tsx.snap index c0a13a9ee..4a4c09074 100644 --- a/public/pages/CreateDetector/components/ConfigureAlerts/components/AlertCondition/__snapshots__/AlertConditionPanel.test.tsx.snap +++ b/public/pages/CreateDetector/components/ConfigureAlerts/components/AlertCondition/__snapshots__/AlertConditionPanel.test.tsx.snap @@ -6,6 +6,81 @@ Object { "baseElement":
+
+
+ +
+
+
+
+ +
+
+
+
+
+
+
+ +
+
+
+

+ Detection rules +

+
+
+
+
@@ -33,7 +108,7 @@ Object {
- Trigger details and condition + Trigger condition
-
-
- -
-
-
-
- -
-
-
-
-
-
-

- If a detection rule matches -

-
-
, "container":
+
+
+ +
+
+
+
+ +
+
+
+
+
+
+
+ +
+
+
+

+ Detection rules +

+
+
+
+
@@ -887,7 +981,7 @@ Object {
- Trigger details and condition + Trigger condition
-
-
- -
-
-
-
- -
-
-
-
-
-
-

- If a detection rule matches -

-
-
{ + return !!trigger.name && validateName(trigger.name) && trigger.severity; + }); + this.props.updateDataValidState(DetectorCreationStep.CONFIGURE_ALERTS, isTriggerDataValid); } }; @@ -139,7 +147,7 @@ export default class ConfigureAlerts extends Component { if (isEdit) { - return <>Alert triggers (${triggers.length}); + return <>{`Alert triggers (${triggers.length})`}; } return ( @@ -155,8 +163,8 @@ export default class ConfigureAlerts extends Component + const content = ( + <> {getPageTitle()} @@ -216,7 +224,9 @@ export default class ConfigureAlerts extends Component= MAX_ALERT_CONDITIONS} onClick={this.addCondition}> {triggers.length > 0 ? 'Add another alert trigger' : 'Add alert triggers'} -
+ ); + + return isEdit ?
{content}
: {content}; } } diff --git a/public/pages/CreateDetector/components/DefineDetector/components/DetectionRules/DetectionRules.tsx b/public/pages/CreateDetector/components/DefineDetector/components/DetectionRules/DetectionRules.tsx index 8b715d12f..2305359c3 100644 --- a/public/pages/CreateDetector/components/DefineDetector/components/DetectionRules/DetectionRules.tsx +++ b/public/pages/CreateDetector/components/DefineDetector/components/DetectionRules/DetectionRules.tsx @@ -98,11 +98,10 @@ export const DetectionRules: React.FC = ({ buttonContent={
-

{`Detection rules (${enabledRulesCount} selected)`}

+

{`Selected detection rules (${enabledRulesCount})`}

- Detection rules are automatically added based on your chosen log types. Additionally, - you may add or remove detection rules for this detector. + Add or remove detection rules for this detector.
} diff --git a/public/pages/CreateDetector/components/DefineDetector/components/DetectorDataSource/DetectorDataSource.tsx b/public/pages/CreateDetector/components/DefineDetector/components/DetectorDataSource/DetectorDataSource.tsx index 10fdef34f..512ac1d59 100644 --- a/public/pages/CreateDetector/components/DefineDetector/components/DetectorDataSource/DetectorDataSource.tsx +++ b/public/pages/CreateDetector/components/DefineDetector/components/DetectorDataSource/DetectorDataSource.tsx @@ -4,7 +4,6 @@ */ import React, { Component } from 'react'; -import { ContentPanel } from '../../../../../../components/ContentPanel'; import { EuiComboBox, EuiComboBoxOptionOption, @@ -12,6 +11,7 @@ import { EuiSpacer, EuiCallOut, EuiTextColor, + EuiTitle, } from '@elastic/eui'; import { FormFieldHeader } from '../../../../../../components/FormFieldHeader/FormFieldHeader'; import { IndexOption } from '../../../../../Detectors/models/interfaces'; @@ -149,7 +149,10 @@ export default class DetectorDataSource extends Component< } = this.state; const isInvalid = fieldTouched && detectorIndices.length < MIN_NUM_DATA_SOURCES; return ( - + <> + +

Data source

+
) : null} -
+ ); } } diff --git a/public/pages/CreateDetector/components/DefineDetector/components/DetectorDetails/DetectorBasicDetailsForm.tsx b/public/pages/CreateDetector/components/DefineDetector/components/DetectorDetails/DetectorBasicDetailsForm.tsx index be6977c86..efcbe5bdc 100644 --- a/public/pages/CreateDetector/components/DefineDetector/components/DetectorDetails/DetectorBasicDetailsForm.tsx +++ b/public/pages/CreateDetector/components/DefineDetector/components/DetectorDetails/DetectorBasicDetailsForm.tsx @@ -4,8 +4,7 @@ */ import React, { Component } from 'react'; -import { ContentPanel } from '../../../../../../components/ContentPanel'; -import { EuiFormRow, EuiFieldText, EuiSpacer, EuiTextArea } from '@elastic/eui'; +import { EuiFormRow, EuiFieldText, EuiSpacer, EuiTextArea, EuiTitle } from '@elastic/eui'; import { FormFieldHeader } from '../../../../../../components/FormFieldHeader/FormFieldHeader'; import { getDescriptionErrorMessage, @@ -74,7 +73,10 @@ export default class DetectorBasicDetailsForm extends Component< nameFieldTouched, } = this.state; return ( - + <> + +

Detector details

+
} @@ -111,7 +113,7 @@ export default class DetectorBasicDetailsForm extends Component< data-test-subj={'define-detector-detector-description'} /> -
+ ); } } diff --git a/public/pages/CreateDetector/components/DefineDetector/components/DetectorSchedule/DetectorSchedule.tsx b/public/pages/CreateDetector/components/DefineDetector/components/DetectorSchedule/DetectorSchedule.tsx index 2816d0ddb..802e60a84 100644 --- a/public/pages/CreateDetector/components/DefineDetector/components/DetectorSchedule/DetectorSchedule.tsx +++ b/public/pages/CreateDetector/components/DefineDetector/components/DetectorSchedule/DetectorSchedule.tsx @@ -3,9 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { ContentPanel } from '../../../../../../components/ContentPanel'; import React from 'react'; -import { EuiSelectOption } from '@elastic/eui'; +import { EuiSelectOption, EuiSpacer, EuiTitle } from '@elastic/eui'; import { PeriodSchedule } from '../../../../../../../models/interfaces'; import { Interval } from './Interval'; import { CustomCron } from './CustomCron'; @@ -52,9 +51,13 @@ export class DetectorSchedule extends React.Component< const FrequencyPicker = components[this.state.selectedFrequency]; return ( - + <> + +

Detector schedule

+
+ -
+ ); } } diff --git a/public/pages/CreateDetector/components/DefineDetector/components/DetectorType/DetectorType.tsx b/public/pages/CreateDetector/components/DefineDetector/components/DetectorType/DetectorType.tsx index 0b98da1f9..646fe4272 100644 --- a/public/pages/CreateDetector/components/DefineDetector/components/DetectorType/DetectorType.tsx +++ b/public/pages/CreateDetector/components/DefineDetector/components/DetectorType/DetectorType.tsx @@ -4,8 +4,7 @@ */ import React, { Component } from 'react'; -import { ContentPanel } from '../../../../../../components/ContentPanel'; -import { EuiFormRow, EuiSpacer, EuiComboBox } from '@elastic/eui'; +import { EuiFormRow, EuiSpacer, EuiComboBox, EuiTitle, EuiText } from '@elastic/eui'; import { FormFieldHeader } from '../../../../../../components/FormFieldHeader/FormFieldHeader'; import { CreateDetectorRulesState, DetectionRules } from '../DetectionRules/DetectionRules'; import { RuleItem } from '../DetectionRules/types/interfaces'; @@ -72,17 +71,21 @@ export default class DetectorType extends Component + <> + +

Detection rules

+
+ +

+ The detection rules are automatically populated based on your selected log type. Threat + intelligence based detection can be enabled for standard log types.{' '} +

+
+ - +
} @@ -117,7 +120,7 @@ export default class DetectorType extends Component - + ); } } diff --git a/public/pages/CreateDetector/components/DefineDetector/components/ThreatIntelligence/ThreatIntelligence.tsx b/public/pages/CreateDetector/components/DefineDetector/components/ThreatIntelligence/ThreatIntelligence.tsx new file mode 100644 index 000000000..9af80bfa6 --- /dev/null +++ b/public/pages/CreateDetector/components/DefineDetector/components/ThreatIntelligence/ThreatIntelligence.tsx @@ -0,0 +1,38 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from 'react'; +import { EuiCheckbox, EuiText, EuiTitle, htmlIdGenerator } from '@elastic/eui'; + +export interface ThreatIntelligenceProps { + threatIntelChecked: boolean; + onThreatIntelChange: (checked: boolean) => void; +} + +export const ThreatIntelligence: React.FC = ({ + threatIntelChecked, + onThreatIntelChange, +}) => { + return ( + <> + +

Threat intelligence feeds

+
+ + +

+ Match your data source against known malicious IP-addresses. Available for standard log + types only. Learn more +

+
+ onThreatIntelChange(e.target.checked)} + /> + + ); +}; diff --git a/public/pages/CreateDetector/components/DefineDetector/containers/DefineDetector.tsx b/public/pages/CreateDetector/components/DefineDetector/containers/DefineDetector.tsx index 7db946786..eaf38743b 100644 --- a/public/pages/CreateDetector/components/DefineDetector/containers/DefineDetector.tsx +++ b/public/pages/CreateDetector/components/DefineDetector/containers/DefineDetector.tsx @@ -5,7 +5,7 @@ import React, { Component } from 'react'; import { RouteComponentProps } from 'react-router-dom'; -import { EuiSpacer, EuiTitle, EuiText, EuiCallOut } from '@elastic/eui'; +import { EuiSpacer, EuiCallOut } from '@elastic/eui'; import { PeriodSchedule } from '../../../../../../models/interfaces'; import DetectorBasicDetailsForm from '../components/DetectorDetails'; import DetectorDataSource from '../components/DetectorDataSource'; @@ -20,6 +20,9 @@ import { NotificationsStart } from 'opensearch-dashboards/public'; import { logTypesWithDashboards } from '../../../../../utils/constants'; import { Detector, DetectorCreationStep, FieldMapping } from '../../../../../../types'; import { ConfigureFieldMappingProps } from '../../ConfigureFieldMapping/containers/ConfigureFieldMapping'; +import { ContentPanel } from '../../../../../components/ContentPanel'; +import { ruleTypes } from '../../../../Rules/utils/constants'; +import { ThreatIntelligence } from '../components/ThreatIntelligence/ThreatIntelligence'; interface DefineDetectorProps extends RouteComponentProps { detector: Detector; @@ -43,6 +46,10 @@ interface DefineDetectorState { } export default class DefineDetector extends Component { + private standardLogTypes = new Set( + ruleTypes.filter((ruleType) => ruleType.isStandard).map(({ value }) => value) + ); + constructor(props: DefineDetectorProps) { super(props); this.state = { @@ -123,6 +130,16 @@ export default class DefineDetector extends Component { + const newDetector: Detector = { + ...this.state.detector, + threat_intel_enabled: checked, }; this.updateDetectorCreationState(newDetector); @@ -186,7 +203,7 @@ export default class DefineDetector extends Component - -

{`${isEdit ? 'Edit' : 'Define'} detector`}

-
- - - Configure your detector to identify relevant security findings and potential threats from - your log data. - - + - + + {this.standardLogTypes.has(detector_type) && ( + + )} + {logTypesWithDashboards.has(detector_type) ? ( <> -
+ ); } } diff --git a/public/pages/CreateDetector/containers/CreateDetector.tsx b/public/pages/CreateDetector/containers/CreateDetector.tsx index 0e1c42d99..5ade7d68a 100644 --- a/public/pages/CreateDetector/containers/CreateDetector.tsx +++ b/public/pages/CreateDetector/containers/CreateDetector.tsx @@ -5,7 +5,15 @@ import React, { Component } from 'react'; import { RouteComponentProps } from 'react-router-dom'; -import { EuiButton, EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiSteps } from '@elastic/eui'; +import { + EuiButton, + EuiButtonEmpty, + EuiFlexGroup, + EuiFlexItem, + EuiSpacer, + EuiSteps, + EuiTitle, +} from '@elastic/eui'; import DefineDetector from '../components/DefineDetector/containers/DefineDetector'; import { createDetectorSteps, PENDING_DETECTOR_ID } from '../utils/constants'; import { @@ -346,7 +354,15 @@ export default class CreateDetector extends Component - {this.getStepContent()} + + <> + +

Create detector

+
+ + {this.getStepContent()} + +
diff --git a/public/pages/Detectors/components/UpdateBasicDetails/UpdateBasicDetails.tsx b/public/pages/Detectors/components/UpdateBasicDetails/UpdateBasicDetails.tsx index 0810b90c7..55e1af2a8 100644 --- a/public/pages/Detectors/components/UpdateBasicDetails/UpdateBasicDetails.tsx +++ b/public/pages/Detectors/components/UpdateBasicDetails/UpdateBasicDetails.tsx @@ -8,6 +8,7 @@ import { EuiComboBoxOptionOption, EuiFlexGroup, EuiFlexItem, + EuiPanel, EuiSpacer, EuiTitle, } from '@elastic/eui'; @@ -244,46 +245,50 @@ export const UpdateDetectorBasicDetails: React.FC + <>

Edit detector details

- - - - - - - - - - {fieldMappingsIsVisible ? ( - <> - - - - ) : null} + + + + + + + + + + + {fieldMappingsIsVisible ? ( + <> + + + + ) : null} + + + @@ -303,6 +308,6 @@ export const UpdateDetectorBasicDetails: React.FC -
+ ); }; diff --git a/public/pages/Detectors/components/UpdateBasicDetails/__snapshots__/UpdateDetectorBasicDetails.test.tsx.snap b/public/pages/Detectors/components/UpdateBasicDetails/__snapshots__/UpdateDetectorBasicDetails.test.tsx.snap index 6dc2300c2..9dd4cfda7 100644 --- a/public/pages/Detectors/components/UpdateBasicDetails/__snapshots__/UpdateDetectorBasicDetails.test.tsx.snap +++ b/public/pages/Detectors/components/UpdateBasicDetails/__snapshots__/UpdateDetectorBasicDetails.test.tsx.snap @@ -584,946 +584,873 @@ exports[` spec renders the component 1`] = ` } } > -
- +

-

- Edit detector details -

-
- -
- - + + +
+ + +
- - +

+ Detector details +

+ + +
+ + } + labelType="label" >
- -
- -
- -

- Detector details -

-
-
-
-
-
- -
-
- -
- - - } - labelType="label" + type="label" > -
-
- - - -
-
- - -
-
- - - - -
-
-
-
-
-
-
- -
- - - } - labelType="label" + + Name + +
+ + + + +
+
+ -
- - - -
-
- -