From 1164b98795c3aa82fbd9bb3ebb94254c627559ed Mon Sep 17 00:00:00 2001 From: Joanne Wang Date: Wed, 11 Sep 2024 15:52:39 -0700 Subject: [PATCH] Fix findings page crash and rule severity correctness (#1160) * bug fix Signed-off-by: Amardeepsingh Siglani * fix correlation finding severity Signed-off-by: Joanne Wang --------- Signed-off-by: Amardeepsingh Siglani Signed-off-by: Joanne Wang Co-authored-by: Amardeepsingh Siglani --- .../Findings/containers/Findings/Findings.tsx | 43 +++++++++++++++---- public/pages/Rules/utils/constants.ts | 36 +++++++++++----- public/store/CorrelationsStore.ts | 14 +++++- 3 files changed, 73 insertions(+), 20 deletions(-) diff --git a/public/pages/Findings/containers/Findings/Findings.tsx b/public/pages/Findings/containers/Findings/Findings.tsx index dc197ad90..c25d5c424 100644 --- a/public/pages/Findings/containers/Findings/Findings.tsx +++ b/public/pages/Findings/containers/Findings/Findings.tsx @@ -28,6 +28,7 @@ import { import { BREADCRUMBS, DEFAULT_DATE_RANGE, + DEFAULT_EMPTY_DATA, FindingTabId, MAX_RECENTLY_USED_TIME_RANGES, } from '../../../../utils/constants'; @@ -49,6 +50,7 @@ import { getDuration, getIsNotificationPluginInstalled, setBreadcrumbs, + isThreatIntelQuery, } from '../../../../utils/helpers'; import { RuleSource } from '../../../../../server/models/interfaces'; import { NotificationsStart } from 'opensearch-dashboards/public'; @@ -66,6 +68,7 @@ import { } from '../../../../../types'; import { ThreatIntelFindingsTable } from '../../components/FindingsTable/ThreatIntelFindingsTable'; import { PageHeader } from '../../../../components/PageHeader/PageHeader'; +import { RuleSeverityValue, RuleSeverityPriority } from '../../../Rules/utils/constants'; interface FindingsProps extends RouteComponentProps, DataSourceProps { detectorService: DetectorsService; @@ -436,7 +439,8 @@ class Findings extends Component { const ruleLevel = finding.detectionType === 'Threat intelligence' ? 'high' - : (findingsState as DetectionRulesFindingsState).rules[finding.queries[0].id].level; + : (findingsState as DetectionRulesFindingsState).rules[finding.queries[0].id]?.level || + DEFAULT_EMPTY_DATA; visData.push({ finding: 1, time: findingTime.getTime(), @@ -521,13 +525,30 @@ class Findings extends Component { } = this.props; if (selectedTabId === FindingTabId.DetectionRules && Object.keys(rules).length > 0) { findings = findings.map((finding: any) => { - const rule = rules[finding.queries[0].id]; - if (rule) { - finding['ruleName'] = rule.title; - finding['ruleSeverity'] = - rule.level === 'critical' ? rule.level : finding['ruleSeverity'] || rule.level; - finding['tags'] = rule.tags; - } + const matchedRules: RuleSource[] = []; + finding.queries.forEach((query: any) => { + if (rules[query.id]) { + matchedRules.push(rules[query.id]); + } + }); + + matchedRules.sort((a, b) => { + return RuleSeverityPriority[a.level as RuleSeverityValue] < + RuleSeverityPriority[b.level as RuleSeverityValue] + ? -1 + : 1; + }); + + finding['ruleName'] = + matchedRules[0]?.title || + (finding.queries.find(({ id }) => isThreatIntelQuery(id)) + ? 'Threat intel' + : DEFAULT_EMPTY_DATA); + finding['ruleSeverity'] = + matchedRules[0]?.level === 'critical' + ? 'critical' + : finding['ruleSeverity'] || matchedRules[0]?.level || DEFAULT_EMPTY_DATA; + finding['tags'] = matchedRules[0]?.tags || []; return finding; }); } @@ -625,7 +646,11 @@ class Findings extends Component { {!findings || findings.length === 0 ? (

No findings

} + title={ + +

No findings

+
+ } body={ {this.state.findingStateByTabId[this.state.selectedTabId].emptyPromptBody} diff --git a/public/pages/Rules/utils/constants.ts b/public/pages/Rules/utils/constants.ts index ce29b4488..d53557225 100644 --- a/public/pages/Rules/utils/constants.ts +++ b/public/pages/Rules/utils/constants.ts @@ -15,6 +15,22 @@ export const ruleTypes: { const paletteColors = euiPaletteForStatus(5); +export enum RuleSeverityValue { + Critical = 'critical', + High = 'high', + Medium = 'medium', + Low = 'low', + Informational = 'informational', +} + +export const RuleSeverityPriority: Record = { + [RuleSeverityValue.Critical]: '1', + [RuleSeverityValue.High]: '2', + [RuleSeverityValue.Medium]: '3', + [RuleSeverityValue.Low]: '4', + [RuleSeverityValue.Informational]: '5', +}; + export const ruleSeverity: { name: string; value: string; @@ -23,32 +39,32 @@ export const ruleSeverity: { }[] = [ { name: 'Critical', - value: 'critical', - priority: '1', + value: RuleSeverityValue.Critical, + priority: RuleSeverityPriority[RuleSeverityValue.Critical], color: { background: paletteColors[4], text: 'white' }, }, { name: 'High', - value: 'high', - priority: '2', + value: RuleSeverityValue.High, + priority: RuleSeverityPriority[RuleSeverityValue.High], color: { background: paletteColors[3], text: 'white' }, }, { name: 'Medium', - value: 'medium', - priority: '3', + value: RuleSeverityValue.Medium, + priority: RuleSeverityPriority[RuleSeverityValue.Medium], color: { background: paletteColors[2], text: 'black' }, }, { name: 'Low', - value: 'low', - priority: '4', + value: RuleSeverityValue.Low, + priority: RuleSeverityPriority[RuleSeverityValue.Low], color: { background: paletteColors[1], text: 'white' }, }, { name: 'Informational', - value: 'informational', - priority: '5', + value: RuleSeverityValue.Informational, + priority: RuleSeverityPriority[RuleSeverityValue.Informational], color: { background: paletteColors[0], text: 'white' }, }, ]; diff --git a/public/store/CorrelationsStore.ts b/public/store/CorrelationsStore.ts index b117d97b1..229fbc91a 100644 --- a/public/store/CorrelationsStore.ts +++ b/public/store/CorrelationsStore.ts @@ -19,6 +19,8 @@ import { NotificationsStart } from 'opensearch-dashboards/public'; import { errorNotificationToast } from '../utils/helpers'; import { DEFAULT_EMPTY_DATA } from '../utils/constants'; import { DataStore } from './DataStore'; +import { RuleSource } from '../../server/models/interfaces'; +import { RuleSeverityPriority, RuleSeverityValue } from '../pages/Rules/utils/constants'; export interface ICorrelationsCache { [key: string]: CorrelationRule[]; @@ -275,7 +277,17 @@ export class CorrelationsStore implements ICorrelationsStore { const findings = await DataStore.findings.getFindingsByIds(findingIds); findings.forEach((f) => { const detector = detectorsMap[f.detectorId]; - const rule = allRules.find((rule) => rule._id === f.queries[0].id); + const queryIds = f.queries.map((query) => query.id); + const matchedRules = allRules.filter((rule) => queryIds.includes(rule._id)); + matchedRules.sort((a, b) => { + return RuleSeverityPriority[a._source.level as RuleSeverityValue] < + RuleSeverityPriority[b._source.level as RuleSeverityValue] + ? -1 + : 1; + }); + + const rule = allRules.find((rule) => rule._id === matchedRules[0]?._id); + findingsMap[f.id] = { ...f, id: f.id,