From 24c7b6211f21b340960f87d95a6e20e06d9cd56d Mon Sep 17 00:00:00 2001 From: Aleksandar Djindjic Date: Mon, 9 Jan 2023 21:04:45 +0100 Subject: [PATCH 1/5] Rule details flyout on detector view page Signed-off-by: Aleksandar Djindjic --- .../DetectorRulesView/DetectorRulesView.tsx | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/public/pages/Detectors/components/DetectorRulesView/DetectorRulesView.tsx b/public/pages/Detectors/components/DetectorRulesView/DetectorRulesView.tsx index 1b3731ce9..adeb6333b 100644 --- a/public/pages/Detectors/components/DetectorRulesView/DetectorRulesView.tsx +++ b/public/pages/Detectors/components/DetectorRulesView/DetectorRulesView.tsx @@ -129,9 +129,21 @@ export const DetectorRulesView: React.FC = (props) => { }); }, [services, props.detector]); + const onRuleDetails = (ruleItem: RuleItem) => { + setFlyoutData(() => ({ + title: ruleItem.name, + level: ruleItem.severity, + category: ruleItem.logType, + description: ruleItem.description, + source: ruleItem.library, + ruleInfo: ruleItem.ruleInfo, + ruleId: ruleItem.id, + })); + }; + const rules = ( `${item.name}`} pagination From 31ff8ac270824f230f1c2560d0178b38adb2e9a1 Mon Sep 17 00:00:00 2001 From: Aleksandar Djindjic Date: Tue, 10 Jan 2023 17:05:17 +0100 Subject: [PATCH 2/5] use RulesViewModelActor on detector view page Signed-off-by: Aleksandar Djindjic --- .../DetectorRulesView/DetectorRulesView.tsx | 56 +++++++------------ .../pages/Rules/models/RulesViewModelActor.ts | 14 +++-- 2 files changed, 30 insertions(+), 40 deletions(-) diff --git a/public/pages/Detectors/components/DetectorRulesView/DetectorRulesView.tsx b/public/pages/Detectors/components/DetectorRulesView/DetectorRulesView.tsx index adeb6333b..c1397b12d 100644 --- a/public/pages/Detectors/components/DetectorRulesView/DetectorRulesView.tsx +++ b/public/pages/Detectors/components/DetectorRulesView/DetectorRulesView.tsx @@ -4,7 +4,7 @@ */ import { ContentPanel } from '../../../../components/ContentPanel'; -import React, { useContext, useEffect, useState } from 'react'; +import React, { useContext, useEffect, useState, useMemo } from 'react'; import { EuiAccordion, EuiButton, EuiInMemoryTable, EuiSpacer, EuiText } from '@elastic/eui'; import { RuleItem } from '../../../CreateDetector/components/DefineDetector/components/DetectionRules/types/interfaces'; import { getRulesColumns } from '../../../CreateDetector/components/DefineDetector/components/DetectionRules/utils/constants'; @@ -16,6 +16,7 @@ import { NotificationsStart } from 'opensearch-dashboards/public'; import { RulesTable } from '../../../Rules/components/RulesTable/RulesTable'; import { RuleTableItem } from '../../../Rules/utils/helpers'; import { RuleViewerFlyout } from '../../../Rules/components/RuleViewerFlyout/RuleViewerFlyout'; +import { RulesViewModelActor } from '../../../Rules/models/RulesViewModelActor'; export interface DetectorRulesViewProps { detector: Detector; @@ -59,33 +60,12 @@ export const DetectorRulesView: React.FC = (props) => { ]; const services = useContext(ServicesContext); - useEffect(() => { - const getRules = async (prePackaged: boolean): Promise => { - const getRulesRes = await services?.ruleService.getRules(prePackaged, { - from: 0, - size: 5000, - query: { - nested: { - path: 'rule', - query: { - bool: { - must: [ - { match: { 'rule.category': `${props.detector.detector_type.toLowerCase()}` } }, - ], - }, - }, - }, - }, - }); - - if (getRulesRes?.ok) { - return getRulesRes.response.hits.hits; - } else { - errorNotificationToast(props.notifications, 'retrieve', 'rules', getRulesRes?.error); - return []; - } - }; + const rulesViewModelActor = useMemo( + () => (services ? new RulesViewModelActor(services?.ruleService) : null), + [services] + ); + useEffect(() => { const updateRulesState = async () => { setLoading(true); const enabledPrePackagedRuleIds = new Set( @@ -95,26 +75,32 @@ export const DetectorRulesView: React.FC = (props) => { props.detector.inputs[0].detector_input.custom_rules.map((ruleInfo) => ruleInfo.id) ); - const prePackagedRules = await getRules(true); - const customRules = await getRules(false); + const allRules = await rulesViewModelActor?.fetchRules(undefined, { + bool: { + must: [{ match: { 'rule.category': `${props.detector.detector_type.toLowerCase()}` } }], + }, + }); + + const prePackagedRules = allRules?.filter((rule) => rule.prePackaged); + const customRules = allRules?.filter((rule) => !rule.prePackaged); - const enabledPrePackagedRules = prePackagedRules.filter((hit: RuleInfo) => { + const enabledPrePackagedRules = prePackagedRules?.filter((hit: RuleInfo) => { return enabledPrePackagedRuleIds.has(hit._id); }); - const enabledCustomRules = customRules.filter((hit: RuleInfo) => { + const enabledCustomRules = customRules?.filter((hit: RuleInfo) => { return enabledCustomRuleIds.has(hit._id); }); const enabledRuleItems = translateToRuleItems( - enabledPrePackagedRules, - enabledCustomRules, + enabledPrePackagedRules || [], + enabledCustomRules || [], props.detector.detector_type, () => true ); const allRuleItems = translateToRuleItems( - prePackagedRules, - customRules, + prePackagedRules || [], + customRules || [], props.detector.detector_type, (ruleInfo) => enabledPrePackagedRuleIds.has(ruleInfo._id) || enabledCustomRuleIds.has(ruleInfo._id) diff --git a/public/pages/Rules/models/RulesViewModelActor.ts b/public/pages/Rules/models/RulesViewModelActor.ts index d7afaaeb8..008026485 100644 --- a/public/pages/Rules/models/RulesViewModelActor.ts +++ b/public/pages/Rules/models/RulesViewModelActor.ts @@ -26,9 +26,12 @@ export class RulesViewModelActor { return this.rulesViewModel.allRules; } - public async fetchRules(terms?: { [key: string]: string[] }): Promise { - let prePackagedRules = await this.getRules(true, terms); - let customRules = await this.getRules(false, terms); + public async fetchRules( + terms?: { [key: string]: string[] }, + query?: any + ): Promise { + let prePackagedRules = await this.getRules(true, terms, query); + let customRules = await this.getRules(false, terms, query); prePackagedRules = this.extractAndAddDetection(prePackagedRules); customRules = this.extractAndAddDetection(customRules); @@ -41,7 +44,8 @@ export class RulesViewModelActor { prePackaged: boolean, terms?: { [key: string]: string[]; - } + }, + query?: any ): Promise { const getRulesRes = await this.service.getRules(prePackaged, { from: 0, @@ -49,7 +53,7 @@ export class RulesViewModelActor { query: { nested: { path: 'rule', - query: { + query: query || { terms: terms ? terms : { From b7f899c808ff8fc321a141878cb4dc3614772bbe Mon Sep 17 00:00:00 2001 From: Aleksandar Djindjic Date: Tue, 10 Jan 2023 18:20:33 +0100 Subject: [PATCH 3/5] migrate create detector to RulesViewModelActor Signed-off-by: Aleksandar Djindjic --- .../containers/CreateDetector.tsx | 71 ++++--------------- 1 file changed, 14 insertions(+), 57 deletions(-) diff --git a/public/pages/CreateDetector/containers/CreateDetector.tsx b/public/pages/CreateDetector/containers/CreateDetector.tsx index 6a782e9b5..146165ffd 100644 --- a/public/pages/CreateDetector/containers/CreateDetector.tsx +++ b/public/pages/CreateDetector/containers/CreateDetector.tsx @@ -29,13 +29,13 @@ import { RuleItem, RuleItemInfo, } from '../components/DefineDetector/components/DetectionRules/types/interfaces'; -import { RuleInfo } from '../../../../server/models/interfaces'; import { NotificationsStart } from 'opensearch-dashboards/public'; import { errorNotificationToast, successNotificationToast, getPlugins, } from '../../../utils/helpers'; +import { RulesViewModelActor } from '../../Rules/models/RulesViewModelActor'; interface CreateDetectorProps extends RouteComponentProps { isEdit: boolean; @@ -55,9 +55,11 @@ interface CreateDetectorState { export default class CreateDetector extends Component { static contextType = CoreServicesContext; + private rulesViewModelActor: RulesViewModelActor; constructor(props: CreateDetectorProps) { super(props); + this.rulesViewModelActor = new RulesViewModelActor(props.services.ruleService); this.state = { currentStep: DetectorCreationStep.DEFINE_DETECTOR, detector: EMPTY_DEFAULT_DETECTOR, @@ -177,13 +179,21 @@ export default class CreateDetector extends Component rule.prePackaged); + const customRules = allRules.filter((rule) => !rule.prePackaged); this.setState({ rulesState: { ...this.state.rulesState, - allRules: customRules.concat(prePackagedRules), + allRules: customRules.concat(prePackagedRules).map((rule) => ({ ...rule, enabled: true })), page: { index: 0, }, @@ -210,59 +220,6 @@ export default class CreateDetector extends Component { - try { - const { detector_type } = this.state.detector; - - if (!detector_type) { - return []; - } - - const rulesRes = await this.props.services.ruleService.getRules(prePackaged, { - from: 0, - size: 5000, - query: { - nested: { - path: 'rule', - query: { - bool: { - must: [{ match: { 'rule.category': `${detector_type}` } }], - }, - }, - }, - }, - }); - - if (rulesRes.ok) { - const rules: RuleItemInfo[] = rulesRes.response.hits.hits.map((ruleInfo: RuleInfo) => { - return { - ...ruleInfo, - enabled: true, - prePackaged, - }; - }); - - return rules; - } else { - errorNotificationToast( - this.props.notifications, - 'retrieve', - `${prePackaged ? 'pre-packaged' : 'custom'}`, - rulesRes.error - ); - return []; - } - } catch (error: any) { - errorNotificationToast( - this.props.notifications, - 'retrieve', - `${prePackaged ? 'pre-packaged' : 'custom'}`, - error - ); - return []; - } - } - onPageChange = (page: { index: number; size: number }) => { this.setState({ rulesState: { From 60af9ab7d77e910c9f9b9d13fe3a37640a166ef6 Mon Sep 17 00:00:00 2001 From: Aleksandar Djindjic Date: Tue, 10 Jan 2023 18:45:18 +0100 Subject: [PATCH 4/5] migrate update detector rules to RulesViewModelActor Signed-off-by: Aleksandar Djindjic --- .../DetectorRulesView/DetectorRulesView.tsx | 2 +- .../components/UpdateRules/UpdateRules.tsx | 117 ++++++------------ 2 files changed, 41 insertions(+), 78 deletions(-) diff --git a/public/pages/Detectors/components/DetectorRulesView/DetectorRulesView.tsx b/public/pages/Detectors/components/DetectorRulesView/DetectorRulesView.tsx index c1397b12d..1cea9a7cc 100644 --- a/public/pages/Detectors/components/DetectorRulesView/DetectorRulesView.tsx +++ b/public/pages/Detectors/components/DetectorRulesView/DetectorRulesView.tsx @@ -61,7 +61,7 @@ export const DetectorRulesView: React.FC = (props) => { const services = useContext(ServicesContext); const rulesViewModelActor = useMemo( - () => (services ? new RulesViewModelActor(services?.ruleService) : null), + () => (services ? new RulesViewModelActor(services.ruleService) : null), [services] ); diff --git a/public/pages/Detectors/components/UpdateRules/UpdateRules.tsx b/public/pages/Detectors/components/UpdateRules/UpdateRules.tsx index 63b7fbb4e..a6806ecba 100644 --- a/public/pages/Detectors/components/UpdateRules/UpdateRules.tsx +++ b/public/pages/Detectors/components/UpdateRules/UpdateRules.tsx @@ -6,11 +6,10 @@ import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiTitle } from '@elastic/eui'; import { DetectorHit, - GetRulesResponse, SearchDetectorsResponse, UpdateDetectorResponse, } from '../../../../../server/models/interfaces'; -import React, { useCallback, useContext, useEffect, useState } from 'react'; +import React, { useCallback, useContext, useEffect, useState, useMemo } from 'react'; import { RouteComponentProps } from 'react-router-dom'; import { RuleItem } from '../../../CreateDetector/components/DefineDetector/components/DetectionRules/types/interfaces'; import { Detector } from '../../../../../models/interfaces'; @@ -22,6 +21,7 @@ import { NotificationsStart } from 'opensearch-dashboards/public'; import { errorNotificationToast, successNotificationToast } from '../../../../utils/helpers'; import { RuleTableItem } from '../../../Rules/utils/helpers'; import { RuleViewerFlyout } from '../../../Rules/components/RuleViewerFlyout/RuleViewerFlyout'; +import { RulesViewModelActor } from '../../../Rules/models/RulesViewModelActor'; export interface UpdateDetectorRulesProps extends RouteComponentProps< @@ -42,6 +42,11 @@ export const UpdateDetectorRules: React.FC = (props) = const detectorId = props.location.pathname.replace(`${ROUTES.EDIT_DETECTOR_RULES}/`, ''); const [flyoutData, setFlyoutData] = useState(null); + const rulesViewModelActor = useMemo( + () => (services ? new RulesViewModelActor(services.ruleService) : null), + [services] + ); + useEffect(() => { const getDetector = async () => { setLoading(true); @@ -62,83 +67,41 @@ export const UpdateDetectorRules: React.FC = (props) = }; const getRules = async (detector: Detector) => { - const prePackagedResponse = (await services?.ruleService.getRules(true, { - from: 0, - size: 5000, - query: { - nested: { - path: 'rule', - query: { - bool: { - must: [{ match: { 'rule.category': `${detector.detector_type.toLowerCase()}` } }], - }, - }, - }, - }, - })) as ServerResponse; - if (prePackagedResponse.ok) { - const ruleInfos = prePackagedResponse.response.hits.hits; - const enabledRuleIds = detector.inputs[0].detector_input.pre_packaged_rules.map( - (rule) => rule.id - ); - const ruleItems = ruleInfos.map((rule) => ({ - name: rule._source.title, - id: rule._id, - severity: rule._source.level, - logType: rule._source.category, - library: 'Sigma', - description: rule._source.description, - active: enabledRuleIds.includes(rule._id), - ruleInfo: rule, - })); - setPrePackagedRuleItems(ruleItems); - } else { - errorNotificationToast( - props.notifications, - 'retrieve', - 'pre-packaged rules', - prePackagedResponse.error - ); - } + const enabledRuleIds = detector.inputs[0].detector_input.pre_packaged_rules.map( + (rule) => rule.id + ); - const customResponse = (await services?.ruleService.getRules(false, { - from: 0, - size: 5000, - query: { - nested: { - path: 'rule', - query: { - bool: { - must: [{ match: { 'rule.category': `${detector.detector_type.toLowerCase()}` } }], - }, - }, - }, + const allRules = await rulesViewModelActor?.fetchRules(undefined, { + bool: { + must: [{ match: { 'rule.category': `${detector.detector_type.toLowerCase()}` } }], }, - })) as ServerResponse; - if (customResponse.ok) { - const ruleInfos = customResponse.response.hits.hits; - const enabledRuleIds = detector.inputs[0].detector_input.custom_rules.map( - (rule) => rule.id - ); - const ruleItems = ruleInfos.map((rule) => ({ - name: rule._source.title, - id: rule._id, - severity: rule._source.level, - logType: rule._source.category, - library: 'Custom', - description: rule._source.description, - active: enabledRuleIds.includes(rule._id), - ruleInfo: rule, - })); - setCustomRuleItems(ruleItems); - } else { - errorNotificationToast( - props.notifications, - 'retrieve', - 'custom rules', - customResponse.error - ); - } + }); + + const prePackagedRules = allRules?.filter((rule) => rule.prePackaged); + const prePackagedRuleItems = prePackagedRules?.map((rule) => ({ + name: rule._source.title, + id: rule._id, + severity: rule._source.level, + logType: rule._source.category, + library: 'Sigma', + description: rule._source.description, + active: enabledRuleIds.includes(rule._id), + ruleInfo: rule, + })); + setPrePackagedRuleItems(prePackagedRuleItems || []); + + const customRules = allRules?.filter((rule) => !rule.prePackaged); + const customRuleItems = customRules?.map((rule) => ({ + name: rule._source.title, + id: rule._id, + severity: rule._source.level, + logType: rule._source.category, + library: 'Custom', + description: rule._source.description, + active: enabledRuleIds.includes(rule._id), + ruleInfo: rule, + })); + setCustomRuleItems(customRuleItems || []); }; const execute = async () => { From 69790c4bb3beca4b682656ea73f70e4cdb1e869d Mon Sep 17 00:00:00 2001 From: Aleksandar Djindjic Date: Tue, 10 Jan 2023 19:08:52 +0100 Subject: [PATCH 5/5] fix create detector 4th step Signed-off-by: Aleksandar Djindjic --- .../DetectorRulesView/DetectorRulesView.tsx | 32 ++++--------------- 1 file changed, 6 insertions(+), 26 deletions(-) diff --git a/public/pages/Detectors/components/DetectorRulesView/DetectorRulesView.tsx b/public/pages/Detectors/components/DetectorRulesView/DetectorRulesView.tsx index 1cea9a7cc..12f7b01d3 100644 --- a/public/pages/Detectors/components/DetectorRulesView/DetectorRulesView.tsx +++ b/public/pages/Detectors/components/DetectorRulesView/DetectorRulesView.tsx @@ -5,9 +5,8 @@ import { ContentPanel } from '../../../../components/ContentPanel'; import React, { useContext, useEffect, useState, useMemo } from 'react'; -import { EuiAccordion, EuiButton, EuiInMemoryTable, EuiSpacer, EuiText } from '@elastic/eui'; +import { EuiAccordion, EuiButton, EuiSpacer, EuiText } from '@elastic/eui'; import { RuleItem } from '../../../CreateDetector/components/DefineDetector/components/DetectionRules/types/interfaces'; -import { getRulesColumns } from '../../../CreateDetector/components/DefineDetector/components/DetectionRules/utils/constants'; import { ServicesContext } from '../../../../services'; import { Detector } from '../../../../../models/interfaces'; import { RuleInfo } from '../../../../../server/models/interfaces'; @@ -115,28 +114,6 @@ export const DetectorRulesView: React.FC = (props) => { }); }, [services, props.detector]); - const onRuleDetails = (ruleItem: RuleItem) => { - setFlyoutData(() => ({ - title: ruleItem.name, - level: ruleItem.severity, - category: ruleItem.logType, - description: ruleItem.description, - source: ruleItem.library, - ruleInfo: ruleItem.ruleInfo, - ruleId: ruleItem.id, - })); - }; - - const rules = ( - `${item.name}`} - pagination - loading={loading} - /> - ); - const getDetectionRulesTitle = () => `View detection rules (${totalSelected})`; const onShowRuleDetails = (rule: RuleTableItem) => { @@ -170,11 +147,14 @@ export const DetectorRulesView: React.FC = (props) => { ruleItems={enabledRuleItems.map((i) => mapRuleItemToRuleTableItem(i))} showRuleDetails={onShowRuleDetails} /> - {rules} ) : ( - {rules} + mapRuleItemToRuleTableItem(i))} + showRuleDetails={onShowRuleDetails} + /> )}