From 235d5ebe537a14914160a86eae3eb0605fd3ef60 Mon Sep 17 00:00:00 2001 From: Angela Chuang <6295984+angorayc@users.noreply.github.com> Date: Sat, 25 Jan 2025 17:44:21 +0800 Subject: [PATCH] [SecuritySolution] Update severity colors for Borealis theme (#206276) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Apply severity colors for Borealis theme. https://github.com/elastic/kibana/pull/204007#discussion_r1908519126 Update: ⚠️ As the final decision for severity color is still pending, the temporary colors are the hard coded color hex: https://github.com/elastic/kibana/issues/203387 `TODO` Borealis migration - move from hardcoded values to severity palette https://github.com/elastic/security-team/issues/11606 | Color token | Amsterdam|Borealis| |-------------|------------|------------| | Critical | euiThemeVars.euiColorDanger |```#E7664C```| | High | euiThemeVars.euiColorVis9_behindText |```#DA8B45``` | | Meduiml |euiThemeVars.euiColorVis5_behindText|```#D6BF57``` | | Low | euiThemeVars.euiColorVis0| ```#54B399``` | ### Running Kibana with the Borealis theme In order to run Kibana with Borealis, you'll need to do the following: Set the following in kibana.dev.yml: uiSettings.experimental.themeSwitcherEnabled: true Run Kibana with the following environment variable set: KBN_OPTIMIZER_THEMES="borealislight,borealisdark,v8light,v8dark" yarn start This will expose a toggle under Stack Management > Advanced Settings > Theme version, which you can use to toggle between Amsterdam and Borealis. ![Image](https://github.com/user-attachments/assets/78d64946-43fc-4400-bbb1-229d900b7f05) ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../components/alerts/alerts_preview.tsx | 2 +- .../alerts_findings_details_table.tsx | 14 +- .../components/charts/donutchart_empty.tsx | 5 +- .../components/severity_badge/index.tsx | 15 +- .../public/common/constants.ts | 24 +- .../common/utils/__mocks__/severity_colors.ts | 18 + .../common/utils/risk_color_palette.test.tsx | 67 ++++ .../common/utils/risk_color_palette.tsx | 47 +++ .../severity_mapping/default_severity.tsx | 3 +- .../severity_mapping/severity_override.tsx | 4 +- .../components/step_about_rule/data.tsx | 63 ++-- .../step_about_rule/default_value.ts | 6 +- .../components/step_about_rule/index.test.tsx | 17 +- .../components/rules_table/__mocks__/mock.ts | 6 +- .../alerts_treemap/index.tsx | 5 +- .../lib/chart_palette/index.test.ts | 36 +- .../alerts_treemap/lib/chart_palette/index.ts | 20 +- .../alerts_treemap/lib/layers/index.test.ts | 10 +- .../legend/get_flattened_legend_items.test.ts | 31 +- .../alerts_treemap/lib/legend/index.test.ts | 14 +- .../chart_panels/chart_collapse/index.tsx | 13 +- .../severity_level_panel/columns.tsx | 76 +++-- .../severity_level_panel/helpers.test.tsx | 24 +- .../severity_level_panel/helpers.tsx | 9 +- .../severity_level_chart.tsx | 20 +- .../detection_engine/rules/helpers.test.tsx | 6 +- .../pages/detection_engine/rules/helpers.tsx | 15 +- .../pages/detection_engine/rules/utils.ts | 6 +- .../components/alert_count_insight.test.tsx | 36 +- .../shared/components/alert_count_insight.tsx | 19 +- .../alerts_by_status/alerts_by_status.tsx | 45 +-- .../host_alerts_table/host_alerts_table.tsx | 322 +++++++++--------- .../rule_alerts_table/rule_alerts_table.tsx | 155 ++++----- .../user_alerts_table/user_alerts_table.tsx | 317 ++++++++--------- .../components/detection_response/utils.tsx | 4 +- 35 files changed, 884 insertions(+), 590 deletions(-) create mode 100644 x-pack/solutions/security/plugins/security_solution/public/common/utils/__mocks__/severity_colors.ts create mode 100644 x-pack/solutions/security/plugins/security_solution/public/common/utils/risk_color_palette.test.tsx create mode 100644 x-pack/solutions/security/plugins/security_solution/public/common/utils/risk_color_palette.tsx diff --git a/x-pack/solutions/security/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.tsx b/x-pack/solutions/security/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.tsx index d9d85db0d4a09..f137ed541781b 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.tsx @@ -87,7 +87,7 @@ export const AlertsPreview = ({ const alertStats = Array.from(severityMap, ([key, count]) => ({ key: capitalize(key), count, - color: getSeverityColor(key), + color: getSeverityColor(key, euiTheme), })); const totalAlertsCount = alertStats.reduce((total, item) => total + item.count, 0); diff --git a/x-pack/solutions/security/plugins/security_solution/public/cloud_security_posture/components/csp_details/alerts_findings_details_table.tsx b/x-pack/solutions/security/plugins/security_solution/public/cloud_security_posture/components/csp_details/alerts_findings_details_table.tsx index bf638a720d822..7a92bc8fdc268 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/cloud_security_posture/components/csp_details/alerts_findings_details_table.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/cloud_security_posture/components/csp_details/alerts_findings_details_table.tsx @@ -9,7 +9,15 @@ import React, { memo, useCallback, useEffect, useState } from 'react'; import { encode } from '@kbn/rison'; import { capitalize } from 'lodash'; import type { Criteria, EuiBasicTableColumn, EuiTableSortingType } from '@elastic/eui'; -import { EuiSpacer, EuiPanel, EuiText, EuiBasicTable, EuiIcon, EuiLink } from '@elastic/eui'; +import { + EuiSpacer, + EuiPanel, + EuiText, + EuiBasicTable, + EuiIcon, + EuiLink, + useEuiTheme, +} from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { DistributionBar } from '@kbn/security-solution-distribution-bar'; import { @@ -78,6 +86,8 @@ interface AlertsDetailsFields { export const AlertsDetailsTable = memo( ({ field, value }: { field: CloudPostureEntityIdentifier; value: string }) => { + const { euiTheme } = useEuiTheme(); + useEffect(() => { uiMetricService.trackUiMetric( METRIC_TYPE.COUNT, @@ -164,7 +174,7 @@ export const AlertsDetailsTable = memo( const alertStats = Array.from(severityMap, ([key, count]) => ({ key: capitalize(key), count, - color: getSeverityColor(key), + color: getSeverityColor(key, euiTheme), filter: () => { setCurrentFilter(key); setQuery( diff --git a/x-pack/solutions/security/plugins/security_solution/public/common/components/charts/donutchart_empty.tsx b/x-pack/solutions/security/plugins/security_solution/public/common/components/charts/donutchart_empty.tsx index a24c487d7d924..b70460e81aa92 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/common/components/charts/donutchart_empty.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/common/components/charts/donutchart_empty.tsx @@ -4,7 +4,6 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import type { EuiThemeComputed } from '@elastic/eui'; import { useEuiTheme } from '@elastic/eui'; import { css } from '@emotion/react'; import React from 'react'; @@ -15,12 +14,10 @@ interface DonutChartEmptyProps { } /* - ** @deprecated use getEmptyDonutColor instead + ** @deprecated */ export const emptyDonutColor = '#FAFBFD'; -export const getEmptyDonutColor = (euiTheme: EuiThemeComputed) => euiTheme.colors.textSubdued; - const EmptyDonutChartComponent: React.FC = ({ size = 90, donutWidth = 20, diff --git a/x-pack/solutions/security/plugins/security_solution/public/common/components/severity_badge/index.tsx b/x-pack/solutions/security/plugins/security_solution/public/common/components/severity_badge/index.tsx index 4c6312761666f..4651afb19ea0a 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/common/components/severity_badge/index.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/common/components/severity_badge/index.tsx @@ -7,18 +7,11 @@ import React from 'react'; import { upperFirst } from 'lodash/fp'; -import { euiLightVars } from '@kbn/ui-theme'; import type { Severity } from '@kbn/securitysolution-io-ts-alerting-types'; +import { useEuiTheme } from '@elastic/eui'; import { HealthTruncateText } from '../health_truncate_text'; - -const { euiColorVis0, euiColorVis5, euiColorVis7, euiColorVis9 } = euiLightVars; -const severityToColorMap: Record = { - low: euiColorVis0, - medium: euiColorVis5, - high: euiColorVis7, - critical: euiColorVis9, -}; +import { useRiskSeverityColors } from '../../utils/risk_color_palette'; interface Props { value: Severity; @@ -29,8 +22,10 @@ const SeverityBadgeComponent: React.FC = ({ value, 'data-test-subj': dataTestSubj = 'severity', }) => { + const { euiTheme } = useEuiTheme(); const displayValue = upperFirst(value); - const color = severityToColorMap[value] ?? 'subdued'; + const severityToColorMap = useRiskSeverityColors(); + const color = severityToColorMap[value] ?? euiTheme.colors.textSubdued; return ( + ({ + themeName: 'EUI_THEME_AMSTERDAM', + } as EuiThemeComputed); + +export const getMockEuiBorealisTheme = () => + ({ + themeName: 'EUI_THEME_BOREALIS', + } as EuiThemeComputed); diff --git a/x-pack/solutions/security/plugins/security_solution/public/common/utils/risk_color_palette.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/common/utils/risk_color_palette.test.tsx new file mode 100644 index 0000000000000..136153d2a760c --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/public/common/utils/risk_color_palette.test.tsx @@ -0,0 +1,67 @@ +/* + * 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 { renderHook } from '@testing-library/react'; +import { getRiskSeverityColors, useRiskSeverityColors } from './risk_color_palette'; +import { useEuiTheme } from '@elastic/eui'; +import { getMockEuiAmsterdamTheme, getMockEuiBorealisTheme } from './__mocks__/severity_colors'; + +jest.mock('@elastic/eui', () => ({ + useEuiTheme: jest.fn(), +})); + +const EXPECTED_SEVERITY_COLOR_AMSTERDAM = { + low: '#54b399', + medium: '#f1d86f', + high: '#ff7e62', + critical: '#bd271e', +}; + +const EXPECTED_SEVERITY_COLOR_BOREALIS = { + low: '#54B399', + medium: '#D6BF57', + high: '#DA8B45', + critical: '#E7664C', +} as const; + +describe('risk_color_palette', () => { + const scenarios = [ + { + mockEuiTheme: getMockEuiAmsterdamTheme(), + themeName: 'Amsterdam', + expected: EXPECTED_SEVERITY_COLOR_AMSTERDAM, + }, + { + mockEuiTheme: getMockEuiBorealisTheme(), + themeName: 'Borealis', + expected: EXPECTED_SEVERITY_COLOR_BOREALIS, + }, + ]; + + describe.each(scenarios)( + '$themeName: getRiskSeverityColors', + ({ mockEuiTheme, themeName, expected }) => { + it(`returns the correct colors for ${themeName} theme`, () => { + expect(getRiskSeverityColors(mockEuiTheme)).toEqual(expected); + }); + } + ); + + describe.each(scenarios)( + '$themeName: useRiskSeverityColors', + ({ mockEuiTheme, themeName, expected }) => { + const useEuiThemeMock = useEuiTheme as jest.Mock; + + it(`returns the correct colors for ${themeName} theme`, () => { + useEuiThemeMock.mockReturnValue({ + euiTheme: mockEuiTheme, + }); + const { result } = renderHook(() => useRiskSeverityColors()); + expect(result.current).toEqual(expected); + }); + } + ); +}); diff --git a/x-pack/solutions/security/plugins/security_solution/public/common/utils/risk_color_palette.tsx b/x-pack/solutions/security/plugins/security_solution/public/common/utils/risk_color_palette.tsx new file mode 100644 index 0000000000000..e0eac26c7fe04 --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/public/common/utils/risk_color_palette.tsx @@ -0,0 +1,47 @@ +/* + * 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 { EuiThemeComputed } from '@elastic/eui'; +import { useEuiTheme } from '@elastic/eui'; +import type { Severity } from '@kbn/securitysolution-io-ts-alerting-types'; +import { useMemo } from 'react'; +import { + RISK_COLOR_CRITICAL, + RISK_COLOR_HIGH, + RISK_COLOR_LOW, + RISK_COLOR_MEDIUM, +} from '../constants'; + +// Temporary solution until we have a decision for color palette https://github.com/elastic/kibana/issues/203387 +export const SEVERITY_COLOR = { + low: '#54B399', + medium: '#D6BF57', + high: '#DA8B45', + critical: '#E7664C', +} as const; + +const isAmsterdam = (euiThemeName: string) => { + return euiThemeName?.toLowerCase().includes('amsterdam'); +}; + +export const getRiskSeverityColors = (euiTheme: EuiThemeComputed) => { + if (euiTheme && isAmsterdam(euiTheme.themeName)) { + return { + low: RISK_COLOR_LOW, + medium: RISK_COLOR_MEDIUM, + high: RISK_COLOR_HIGH, + critical: RISK_COLOR_CRITICAL, + }; + } + return SEVERITY_COLOR; +}; + +export const useRiskSeverityColors = (): Record => { + const { euiTheme } = useEuiTheme(); + + return useMemo(() => getRiskSeverityColors(euiTheme), [euiTheme]); +}; diff --git a/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_creation_ui/components/severity_mapping/default_severity.tsx b/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_creation_ui/components/severity_mapping/default_severity.tsx index 477b4bcd0f519..b33bd1c737a5a 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_creation_ui/components/severity_mapping/default_severity.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_creation_ui/components/severity_mapping/default_severity.tsx @@ -15,7 +15,7 @@ import { } from '@elastic/eui'; import React from 'react'; import type { Severity } from '../../../../../common/api/detection_engine/model/rule_schema/common_attributes.gen'; -import { severityOptions } from '../step_about_rule/data'; +import { useSeverityOptions } from '../step_about_rule/data'; import * as i18n from './translations'; const describedByIds = ['detectionEngineStepAboutRuleSeverity']; @@ -26,6 +26,7 @@ interface DefaultSeverityProps { } export function DefaultSeverity({ value, onChange }: DefaultSeverityProps) { + const severityOptions = useSeverityOptions(); return ( diff --git a/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_about_rule/data.tsx b/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_about_rule/data.tsx index 511f4109f9413..57e824d7d400d 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_about_rule/data.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_about_rule/data.tsx @@ -5,22 +5,20 @@ * 2.0. */ -import React from 'react'; +import React, { useMemo } from 'react'; import styled from 'styled-components'; -import { EuiHealth } from '@elastic/eui'; +import type { EuiThemeComputed } from '@elastic/eui'; +import { EuiHealth, useEuiTheme } from '@elastic/eui'; import type { Severity } from '@kbn/securitysolution-io-ts-alerting-types'; import * as I18n from './translations'; import { - RISK_COLOR_LOW, - RISK_COLOR_MEDIUM, - RISK_COLOR_HIGH, - RISK_COLOR_CRITICAL, RISK_SCORE_LOW, RISK_SCORE_MEDIUM, RISK_SCORE_HIGH, RISK_SCORE_CRITICAL, } from '../../../../common/constants'; +import { getRiskSeverityColors } from '../../../../common/utils/risk_color_palette'; export interface SeverityOptionItem { value: Severity; @@ -31,24 +29,41 @@ const StyledEuiHealth = styled(EuiHealth)` line-height: inherit; `; -export const severityOptions: SeverityOptionItem[] = [ - { - value: 'low', - inputDisplay: {I18n.LOW}, - }, - { - value: 'medium', - inputDisplay: {I18n.MEDIUM}, - }, - { - value: 'high', - inputDisplay: {I18n.HIGH}, - }, - { - value: 'critical', - inputDisplay: {I18n.CRITICAL}, - }, -]; +export enum SeverityLevel { + low = 'low', + medium = 'medium', + high = 'high', + critical = 'critical', +} + +const getSeverityOptions: (euiTheme: EuiThemeComputed) => SeverityOptionItem[] = (euiTheme) => { + const palette = getRiskSeverityColors(euiTheme); + return [ + { + value: SeverityLevel.low, + inputDisplay: {I18n.LOW}, + }, + { + value: SeverityLevel.medium, + inputDisplay: {I18n.MEDIUM}, + }, + { + value: SeverityLevel.high, + inputDisplay: {I18n.HIGH}, + }, + { + value: SeverityLevel.critical, + inputDisplay: {I18n.CRITICAL}, + }, + ]; +}; + +export const useSeverityOptions = () => { + const { euiTheme } = useEuiTheme(); + const severityOptions = useMemo(() => getSeverityOptions(euiTheme), [euiTheme]); + + return severityOptions; +}; export const defaultRiskScoreBySeverity: Record = { low: RISK_SCORE_LOW, diff --git a/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_about_rule/default_value.ts b/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_about_rule/default_value.ts index 9d1fff5b8a9ef..e397281b684bc 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_about_rule/default_value.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_about_rule/default_value.ts @@ -23,7 +23,11 @@ export const stepAboutDefaultValue: AboutStepRule = { description: '', isAssociatedToEndpointList: false, isBuildingBlock: false, - severity: { value: 'low', mapping: fillEmptySeverityMappings([]), isMappingChecked: false }, + severity: { + value: 'low', + mapping: fillEmptySeverityMappings([]), + isMappingChecked: false, + }, riskScore: { value: 21, mapping: [], isMappingChecked: false }, references: [''], falsePositives: [''], diff --git a/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_about_rule/index.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_about_rule/index.test.tsx index 94e230c59569c..d101b771e9fe1 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_about_rule/index.test.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_about_rule/index.test.tsx @@ -103,9 +103,10 @@ describe('StepAboutRuleComponent', () => { defineStepDefaultOverride?: DefineStepRule; }) => { const defineStepDefault = defineStepDefaultOverride ?? stepDefineDefaultValue; + const aboutStepDefault = stepAboutDefaultValue; const { aboutStepForm } = useRuleForms({ defineStepDefault, - aboutStepDefault: stepAboutDefaultValue, + aboutStepDefault, scheduleStepDefault: defaultSchedule, actionsStepDefault: stepActionsDefaultValue, }); @@ -118,7 +119,7 @@ describe('StepAboutRuleComponent', () => { machineLearningJobId={defineStepDefault.machineLearningJobId} index={defineStepDefault.index} dataViewId={defineStepDefault.dataViewId} - timestampOverride={stepAboutDefaultValue.timestampOverride} + timestampOverride={aboutStepDefault.timestampOverride} isLoading={false} form={aboutStepForm} /> @@ -287,7 +288,11 @@ describe('StepAboutRuleComponent', () => { setup: '', references: [''], riskScore: { value: 21, mapping: [], isMappingChecked: false }, - severity: { value: 'low', mapping: fillEmptySeverityMappings([]), isMappingChecked: false }, + severity: { + value: 'low', + mapping: fillEmptySeverityMappings([]), + isMappingChecked: false, + }, tags: [], threat: [ { @@ -349,7 +354,11 @@ describe('StepAboutRuleComponent', () => { setup: '', references: [''], riskScore: { value: 80, mapping: [], isMappingChecked: false }, - severity: { value: 'low', mapping: fillEmptySeverityMappings([]), isMappingChecked: false }, + severity: { + value: 'low', + mapping: fillEmptySeverityMappings([]), + isMappingChecked: false, + }, tags: [], threat: [ { diff --git a/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/__mocks__/mock.ts b/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/__mocks__/mock.ts index 25b2d88c6dddd..9fc852882c792 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/__mocks__/mock.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/__mocks__/mock.ts @@ -205,7 +205,11 @@ export const mockAboutStepRule = (): AboutStepRule => ({ name: 'Query with rule-id', description: '24/7', riskScore: { value: 21, mapping: [], isMappingChecked: false }, - severity: { value: 'low', mapping: fillEmptySeverityMappings([]), isMappingChecked: false }, + severity: { + value: 'low', + mapping: fillEmptySeverityMappings([]), + isMappingChecked: false, + }, references: ['www.test.co'], falsePositives: ['test'], tags: ['tag1', 'tag2'], diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/alerts_treemap_panel/alerts_treemap/index.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/alerts_treemap_panel/alerts_treemap/index.tsx index ef7000f5037b3..5cdf30f4a6e0d 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/alerts_treemap_panel/alerts_treemap/index.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/alerts_treemap_panel/alerts_treemap/index.tsx @@ -7,7 +7,7 @@ import type { Datum, ElementClickListener, PartialTheme } from '@elastic/charts'; import { Chart, Partition, PartitionLayout, Settings } from '@elastic/charts'; -import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, useEuiTheme } from '@elastic/eui'; import { isEmpty } from 'lodash/fp'; import React, { useCallback, useMemo } from 'react'; import styled from 'styled-components'; @@ -60,6 +60,7 @@ const AlertsTreemapComponent: React.FC = ({ stackByField0, stackByField1, }: Props) => { + const { euiTheme } = useEuiTheme(); const { theme, baseTheme } = useThemes(); const fillColor = useMemo( () => theme?.background?.color ?? baseTheme.background.color, @@ -101,7 +102,7 @@ const AlertsTreemapComponent: React.FC = ({ [buckets, maxRiskSubAggregations, stackByField0] ); - const colorPalette = useMemo(() => getRiskScorePalette(RISK_SCORE_STEPS), []); + const colorPalette = useMemo(() => getRiskScorePalette(RISK_SCORE_STEPS, euiTheme), [euiTheme]); const legendItems: LegendItem[] = useMemo( () => diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/alerts_treemap_panel/alerts_treemap/lib/chart_palette/index.test.ts b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/alerts_treemap_panel/alerts_treemap/lib/chart_palette/index.test.ts index 64bd8911ddcd6..d7974ac8f4a51 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/alerts_treemap_panel/alerts_treemap/lib/chart_palette/index.test.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/alerts_treemap_panel/alerts_treemap/lib/chart_palette/index.test.ts @@ -5,24 +5,26 @@ * 2.0. */ -import { euiPaletteWarm } from '@elastic/eui'; +import { euiPaletteWarm, useEuiTheme } from '@elastic/eui'; import { - RISK_COLOR_LOW, - RISK_COLOR_MEDIUM, - RISK_COLOR_HIGH, - RISK_COLOR_CRITICAL, RISK_SCORE_MEDIUM, RISK_SCORE_HIGH, RISK_SCORE_CRITICAL, } from '../../../../../../../common/constants'; import { getFillColor, getRiskScorePalette, RISK_SCORE_STEPS } from '.'; +import { renderHook } from '@testing-library/react'; +import { getRiskSeverityColors } from '../../../../../../../common/utils/risk_color_palette'; describe('getFillColor', () => { + const { result } = renderHook(() => useEuiTheme()); + const euiTheme = result.current.euiTheme; + describe('when using the Risk Score palette', () => { - const colorPalette = getRiskScorePalette(RISK_SCORE_STEPS); + const colorPalette = getRiskScorePalette(RISK_SCORE_STEPS, euiTheme); + const expectedColorPalette = getRiskSeverityColors(euiTheme); it('returns the expected fill color', () => { - expect(getFillColor({ riskScore: 50, colorPalette })).toEqual('#d6bf57'); + expect(getFillColor({ riskScore: 50, colorPalette })).toEqual('#f1d86f'); }); it('returns the expected fill color when risk score is zero', () => { @@ -34,50 +36,52 @@ describe('getFillColor', () => { }); it('returns the expected fill color when risk score is 100', () => { - expect(getFillColor({ riskScore: 100, colorPalette })).toEqual('#e7664c'); + expect(getFillColor({ riskScore: 100, colorPalette })).toEqual('#bd271e'); }); it('returns the expected fill color when risk score is greater than 100', () => { - expect(getFillColor({ riskScore: 101, colorPalette })).toEqual('#e7664c'); + expect(getFillColor({ riskScore: 101, colorPalette })).toEqual('#bd271e'); }); it('returns the expected fill color when risk score is greater than RISK_SCORE_CRITICAL', () => { expect(getFillColor({ riskScore: RISK_SCORE_CRITICAL + 1, colorPalette })).toEqual( - RISK_COLOR_CRITICAL + expectedColorPalette.critical ); }); it('returns the expected fill color when risk score is equal to RISK_SCORE_CRITICAL', () => { expect(getFillColor({ riskScore: RISK_SCORE_CRITICAL, colorPalette })).toEqual( - RISK_COLOR_CRITICAL + expectedColorPalette.critical ); }); it('returns the expected fill color when risk score is greater than RISK_SCORE_HIGH', () => { expect(getFillColor({ riskScore: RISK_SCORE_HIGH + 1, colorPalette })).toEqual( - RISK_COLOR_HIGH + expectedColorPalette.high ); }); it('returns the expected fill color when risk score is equal to RISK_SCORE_HIGH', () => { - expect(getFillColor({ riskScore: RISK_SCORE_HIGH, colorPalette })).toEqual(RISK_COLOR_HIGH); + expect(getFillColor({ riskScore: RISK_SCORE_HIGH, colorPalette })).toEqual( + expectedColorPalette.high + ); }); it('returns the expected fill color when risk score is greater than RISK_SCORE_MEDIUM', () => { expect(getFillColor({ riskScore: RISK_SCORE_MEDIUM + 1, colorPalette })).toEqual( - RISK_COLOR_MEDIUM + expectedColorPalette.medium ); }); it('returns the expected fill color when risk score is equal to RISK_SCORE_MEDIUM', () => { expect(getFillColor({ riskScore: RISK_SCORE_MEDIUM, colorPalette })).toEqual( - RISK_COLOR_MEDIUM + expectedColorPalette.medium ); }); it('returns the expected fill color when risk score is less than RISK_SCORE_MEDIUM', () => { expect(getFillColor({ riskScore: RISK_SCORE_MEDIUM - 1, colorPalette })).toEqual( - RISK_COLOR_LOW + expectedColorPalette.low ); }); }); diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/alerts_treemap_panel/alerts_treemap/lib/chart_palette/index.ts b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/alerts_treemap_panel/alerts_treemap/lib/chart_palette/index.ts index 7cc31c76b112e..fdbeb989632e3 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/alerts_treemap_panel/alerts_treemap/lib/chart_palette/index.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/alerts_treemap_panel/alerts_treemap/lib/chart_palette/index.ts @@ -7,15 +7,13 @@ import { clamp } from 'lodash/fp'; +import type { EuiThemeComputed } from '@elastic/eui'; import { - RISK_COLOR_LOW, - RISK_COLOR_MEDIUM, - RISK_COLOR_HIGH, - RISK_COLOR_CRITICAL, RISK_SCORE_MEDIUM, RISK_SCORE_HIGH, RISK_SCORE_CRITICAL, } from '../../../../../../../common/constants'; +import { getRiskSeverityColors } from '../../../../../../../common/utils/risk_color_palette'; /** * The detection engine creates risk scores in the range 1 - 100. @@ -34,20 +32,22 @@ export const RISK_SCORE_STEPS = 101; * This pallet has the same type as `EuiPalette`, which is not exported by * EUI at the time of this writing. */ -export const getRiskScorePalette = (steps: number): string[] => - Array(steps) +export const getRiskScorePalette = (steps: number, euiTheme: EuiThemeComputed): string[] => { + const palette = getRiskSeverityColors(euiTheme); + return Array(steps) .fill(0) .map((_, i) => { if (i >= RISK_SCORE_CRITICAL) { - return RISK_COLOR_CRITICAL; + return palette.critical; } else if (i >= RISK_SCORE_HIGH) { - return RISK_COLOR_HIGH; + return palette.high; } else if (i >= RISK_SCORE_MEDIUM) { - return RISK_COLOR_MEDIUM; + return palette.medium; } else { - return RISK_COLOR_LOW; + return palette.low; } }); +}; /** Returns a fill color based on the index of the risk score in the color palette */ export const getFillColor = ({ diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/alerts_treemap_panel/alerts_treemap/lib/layers/index.test.ts b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/alerts_treemap_panel/alerts_treemap/lib/layers/index.test.ts index f8404cc7d33ed..24f40ee94067f 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/alerts_treemap_panel/alerts_treemap/lib/layers/index.test.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/alerts_treemap_panel/alerts_treemap/lib/layers/index.test.ts @@ -9,9 +9,13 @@ import { getRiskScorePalette, RISK_SCORE_STEPS } from '../chart_palette'; import { maxRiskSubAggregations } from '../flatten/mocks/mock_buckets'; import { getGroupFromPath, getLayersOneDimension, getLayersMultiDimensional } from '.'; import type { Key, ArrayNode } from '@elastic/charts'; +import { useEuiTheme } from '@elastic/eui'; +import { renderHook } from '@testing-library/react'; describe('layers', () => { - const colorPalette = getRiskScorePalette(RISK_SCORE_STEPS); + const { result } = renderHook(() => useEuiTheme()); + const euiTheme = result.current.euiTheme; + const colorPalette = getRiskScorePalette(RISK_SCORE_STEPS, euiTheme); describe('getGroupFromPath', () => { it('returns the expected group from the path', () => { @@ -69,7 +73,7 @@ describe('layers', () => { const dataName = 'mimikatz process started'; expect( getLayersOneDimension({ colorPalette, maxRiskSubAggregations })[0].shape.fillColor(dataName) - ).toEqual('#e7664c'); + ).toEqual('#bd271e'); }); it('return the default fill color when dataName is not found in the maxRiskSubAggregations', () => { @@ -165,7 +169,7 @@ describe('layers', () => { { index: 0, value: 'Host-k8iyfzraq9' }, ], }) - ).toEqual('#e7664c'); + ).toEqual('#bd271e'); }); it('returns the default fillColor for layer 1 when the group from path is not found', () => { diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/alerts_treemap_panel/alerts_treemap/lib/legend/get_flattened_legend_items.test.ts b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/alerts_treemap_panel/alerts_treemap/lib/legend/get_flattened_legend_items.test.ts index c0fb3383a2556..0702b3b98b738 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/alerts_treemap_panel/alerts_treemap/lib/legend/get_flattened_legend_items.test.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/alerts_treemap_panel/alerts_treemap/lib/legend/get_flattened_legend_items.test.ts @@ -13,6 +13,8 @@ import { getFlattenedLegendItems } from './get_flattened_legend_items'; import { bucketsWithStackByField1, maxRiskSubAggregations } from '../flatten/mocks/mock_buckets'; import { flattenedBuckets } from '../flatten/mocks/mock_flattened_buckets'; import { TableId } from '@kbn/securitysolution-data-table'; +import { renderHook } from '@testing-library/react'; +import { useEuiTheme } from '@elastic/eui'; describe('getFlattenedLegendItems', () => { it('returns the expected legend items', () => { @@ -52,25 +54,25 @@ describe('getFlattenedLegendItems', () => { value: 'EQL process sequence', }, { - color: '#da8b45', + color: '#ff7e62', count: 10, field: 'host.name', value: 'Host-k8iyfzraq9', }, { - color: '#da8b45', + color: '#ff7e62', count: 7, field: 'host.name', value: 'Host-ao1a4wu7vn', }, { - color: '#da8b45', + color: '#ff7e62', count: 5, field: 'host.name', value: 'Host-3fbljiq8rj', }, { - color: '#da8b45', + color: '#ff7e62', count: 3, field: 'host.name', value: 'Host-r4y6xi92ob', @@ -81,25 +83,25 @@ describe('getFlattenedLegendItems', () => { value: 'Endpoint Security', }, { - color: '#d6bf57', + color: '#f1d86f', count: 11, field: 'host.name', value: 'Host-ao1a4wu7vn', }, { - color: '#d6bf57', + color: '#f1d86f', count: 6, field: 'host.name', value: 'Host-3fbljiq8rj', }, { - color: '#d6bf57', + color: '#f1d86f', count: 1, field: 'host.name', value: 'Host-k8iyfzraq9', }, { - color: '#d6bf57', + color: '#f1d86f', count: 1, field: 'host.name', value: 'Host-r4y6xi92ob', @@ -110,19 +112,19 @@ describe('getFlattenedLegendItems', () => { value: 'mimikatz process started', }, { - color: '#e7664c', + color: '#bd271e', count: 3, field: 'host.name', value: 'Host-k8iyfzraq9', }, { - color: '#e7664c', + color: '#bd271e', count: 1, field: 'host.name', value: 'Host-3fbljiq8rj', }, { - color: '#e7664c', + color: '#bd271e', count: 1, field: 'host.name', value: 'Host-r4y6xi92ob', @@ -133,16 +135,17 @@ describe('getFlattenedLegendItems', () => { value: 'Threshold rule', }, { - color: '#e7664c', + color: '#bd271e', count: 1, field: 'host.name', value: 'Host-r4y6xi92ob', }, ].map((legend) => ({ ...legend, scopeId: TableId.alertsOnAlertsPage })); - + const { result: euiThemeResult } = renderHook(() => useEuiTheme()); + const euiTheme = euiThemeResult.current.euiTheme; const legendItems = getFlattenedLegendItems({ buckets: bucketsWithStackByField1, - colorPalette: getRiskScorePalette(RISK_SCORE_STEPS), + colorPalette: getRiskScorePalette(RISK_SCORE_STEPS, euiTheme), flattenedBuckets, maxRiskSubAggregations, stackByField0: 'kibana.alert.rule.name', diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/alerts_treemap_panel/alerts_treemap/lib/legend/index.test.ts b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/alerts_treemap_panel/alerts_treemap/lib/legend/index.test.ts index fe136924576a8..deeff81b97d2a 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/alerts_treemap_panel/alerts_treemap/lib/legend/index.test.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/alerts_treemap_panel/alerts_treemap/lib/legend/index.test.ts @@ -18,9 +18,13 @@ import { } from '.'; import type { FlattenedBucket, RawBucket } from '../../types'; import { TableId } from '@kbn/securitysolution-data-table'; +import { renderHook } from '@testing-library/react'; +import { useEuiTheme } from '@elastic/eui'; describe('legend', () => { - const colorPalette = getRiskScorePalette(RISK_SCORE_STEPS); + const { result } = renderHook(() => useEuiTheme()); + const euiTheme = result.current.euiTheme; + const colorPalette = getRiskScorePalette(RISK_SCORE_STEPS, euiTheme); describe('getLegendItemFromRawBucket', () => { const bucket: RawBucket = { @@ -219,28 +223,28 @@ describe('legend', () => { scopeId: TableId.alertsOnAlertsPage, }, { - color: '#da8b45', + color: '#ff7e62', count: 28, field: 'kibana.alert.rule.name', value: 'EQL process sequence', scopeId: TableId.alertsOnAlertsPage, }, { - color: '#d6bf57', + color: '#f1d86f', count: 19, field: 'kibana.alert.rule.name', value: 'Endpoint Security', scopeId: TableId.alertsOnAlertsPage, }, { - color: '#e7664c', + color: '#bd271e', count: 5, field: 'kibana.alert.rule.name', value: 'mimikatz process started', scopeId: TableId.alertsOnAlertsPage, }, { - color: '#e7664c', + color: '#bd271e', count: 1, field: 'kibana.alert.rule.name', value: 'Threshold rule', diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/chart_panels/chart_collapse/index.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/chart_panels/chart_collapse/index.tsx index 0539b7f7615d6..54853d6402e86 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/chart_panels/chart_collapse/index.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/chart_panels/chart_collapse/index.tsx @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { EuiFlexGroup, EuiFlexItem, EuiHealth, EuiText } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiHealth, EuiText, useEuiTheme } from '@elastic/eui'; import { ALERT_SEVERITY, ALERT_RULE_NAME } from '@kbn/rule-data-utils'; import type { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/types'; import type { Filter, Query } from '@kbn/es-query'; @@ -20,8 +20,7 @@ import { getSeverityColor } from '../../severity_level_panel/helpers'; import { FormattedCount } from '../../../../../common/components/formatted_number'; import { getIsChartCollapseData } from './helpers'; import * as i18n from './translations'; - -import { SEVERITY_COLOR } from '../../../../../overview/components/detection_response/utils'; +import { useRiskSeverityColors } from '../../../../../common/utils/risk_color_palette'; const DETECTIONS_ALERTS_COLLAPSED_CHART_ID = 'detectioin-alerts-collapsed-chart'; @@ -79,6 +78,7 @@ export const ChartCollapse: React.FC = ({ signalIndexName, runtimeMappings, }: Props) => { + const { euiTheme } = useEuiTheme(); const uniqueQueryId = useMemo(() => `${DETECTIONS_ALERTS_COLLAPSED_CHART_ID}-${uuid()}`, []); const aggregations = useMemo(() => combinedAggregations(groupBySelection), [groupBySelection]); @@ -94,9 +94,10 @@ export const ChartCollapse: React.FC = ({ const topRule = useMemo(() => data.at(0)?.rule ?? i18n.NO_RESULT_MESSAGE, [data]); const topGroup = useMemo(() => data.at(0)?.group ?? i18n.NO_RESULT_MESSAGE, [data]); + const severityColors = useRiskSeverityColors(); const severities = useMemo(() => { const severityData = data.at(0)?.severities ?? []; - return Object.keys(SEVERITY_COLOR).map((severity) => { + return Object.keys(severityColors).map((severity) => { const obj = severityData.find((s) => s.key === severity); if (obj) { return { key: obj.key, label: obj.label, value: obj.value }; @@ -104,7 +105,7 @@ export const ChartCollapse: React.FC = ({ return { key: severity, label: capitalize(severity), value: 0 }; } }); - }, [data]); + }, [data, severityColors]); const groupBy = useMemo(() => getGroupByLabel(groupBySelection), [groupBySelection]); return ( @@ -115,7 +116,7 @@ export const ChartCollapse: React.FC = ({ {severities.map((severity) => ( - + {`${severity.label}: `} diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/severity_level_panel/columns.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/severity_level_panel/columns.tsx index 2c79054a4a5e9..c4e1c52799607 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/severity_level_panel/columns.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/severity_level_panel/columns.tsx @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React from 'react'; +import React, { useMemo } from 'react'; import { EuiHealth, EuiText } from '@elastic/eui'; import { capitalize } from 'lodash'; import { ALERT_SEVERITY } from '@kbn/rule-data-utils'; @@ -13,41 +13,47 @@ import type { Severity } from '@kbn/securitysolution-io-ts-alerting-types'; import { TableId } from '@kbn/securitysolution-data-table'; import type { SeverityBuckets as SeverityData } from '../../../../overview/components/detection_response/alerts_by_status/types'; import { DefaultDraggable } from '../../../../common/components/draggables'; -import { SEVERITY_COLOR } from '../../../../overview/components/detection_response/utils'; import { FormattedCount } from '../../../../common/components/formatted_number'; import { COUNT_TABLE_TITLE } from '../alerts_count_panel/translations'; import * as i18n from './translations'; +import { useRiskSeverityColors } from '../../../../common/utils/risk_color_palette'; -export const getSeverityTableColumns = (): Array> => [ - { - field: 'key', - name: i18n.SEVERITY_LEVEL_COLUMN_TITLE, - 'data-test-subj': 'severityTable-severity', - render: (severity: Severity) => ( - - - - ), - }, - { - field: 'value', - name: COUNT_TABLE_TITLE, - dataType: 'number', - 'data-test-subj': 'severityTable-alertCount', - width: '45%', - render: (alertCount: number) => ( - - - - ), - }, -]; +export const useGetSeverityTableColumns = (): Array> => { + const severityColors = useRiskSeverityColors(); + return useMemo( + () => [ + { + field: 'key', + name: i18n.SEVERITY_LEVEL_COLUMN_TITLE, + 'data-test-subj': 'severityTable-severity', + render: (severity: Severity) => ( + + + + ), + }, + { + field: 'value', + name: COUNT_TABLE_TITLE, + dataType: 'number', + 'data-test-subj': 'severityTable-alertCount', + width: '45%', + render: (alertCount: number) => ( + + + + ), + }, + ], + [severityColors] + ); +}; diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/severity_level_panel/helpers.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/severity_level_panel/helpers.test.tsx index 9e493ba8fe48f..f0ed436159ec0 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/severity_level_panel/helpers.test.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/severity_level_panel/helpers.test.tsx @@ -4,10 +4,14 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { parseSeverityData } from './helpers'; +import { getSeverityColor, parseSeverityData } from './helpers'; import * as mock from './mock_data'; import type { AlertsBySeverityAgg } from './types'; import type { AlertSearchResponse } from '../../../containers/detection_engine/alerts/types'; +import { + getMockEuiAmsterdamTheme, + getMockEuiBorealisTheme, +} from '../../../../common/utils/__mocks__/severity_colors'; describe('parse severity data', () => { test('parse alerts with data', () => { @@ -24,3 +28,21 @@ describe('parse severity data', () => { expect(res).toEqual([]); }); }); + +describe.each([ + { severity: 'critical', colorToken: '#bd271e', themeName: 'Amsterdam' }, + { severity: 'high', colorToken: '#ff7e62', themeName: 'Amsterdam' }, + { severity: 'medium', colorToken: '#f1d86f', themeName: 'Amsterdam' }, + { severity: 'low', colorToken: '#54b399', themeName: 'Amsterdam' }, + { severity: 'critical', colorToken: '#E7664C', themeName: 'Borealis' }, + { severity: 'high', colorToken: '#DA8B45', themeName: 'Borealis' }, + { severity: 'medium', colorToken: '#D6BF57', themeName: 'Borealis' }, + { severity: 'low', colorToken: '#54B399', themeName: 'Borealis' }, +])('$themeName: getSeverityColor', ({ severity, colorToken, themeName }) => { + const mockEuiTheme = + themeName === 'Amsterdam' ? getMockEuiAmsterdamTheme() : getMockEuiBorealisTheme(); + + test(`returns color for given severity: ${severity}`, () => { + expect(getSeverityColor(severity, mockEuiTheme)).toEqual(colorToken); + }); +}); diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/severity_level_panel/helpers.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/severity_level_panel/helpers.tsx index 227fde8a9b8f2..7b76ff3bb94dc 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/severity_level_panel/helpers.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/severity_level_panel/helpers.tsx @@ -6,18 +6,19 @@ */ import type { Severity } from '@kbn/securitysolution-io-ts-alerting-types'; import { has } from 'lodash'; +import type { EuiThemeComputed } from '@elastic/eui'; import type { AlertsBySeverityAgg } from './types'; import type { AlertSearchResponse } from '../../../containers/detection_engine/alerts/types'; import type { SeverityBuckets as SeverityData } from '../../../../overview/components/detection_response/alerts_by_status/types'; import type { SummaryChartsData, SummaryChartsAgg } from '../alerts_summary_charts_panel/types'; import { severityLabels } from '../../../../overview/components/detection_response/alerts_by_status/use_alerts_by_status'; -import { emptyDonutColor } from '../../../../common/components/charts/donutchart_empty'; -import { SEVERITY_COLOR } from '../../../../overview/components/detection_response/utils'; +import { getRiskSeverityColors } from '../../../../common/utils/risk_color_palette'; const SEVERITY_ORDER: Severity[] = ['critical', 'high', 'medium', 'low']; -export const getSeverityColor = (severity: string) => { - return SEVERITY_COLOR[severity.toLocaleLowerCase() as Severity] ?? emptyDonutColor; +export const getSeverityColor = (severity: string, euiTheme: EuiThemeComputed) => { + const palette = getRiskSeverityColors(euiTheme); + return palette[severity.toLocaleLowerCase() as Severity] ?? euiTheme.colors.textSubdued; }; export const parseSeverityData = ( diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/severity_level_panel/severity_level_chart.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/severity_level_panel/severity_level_chart.tsx index 841586ebad2be..6d2337a78296f 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/severity_level_panel/severity_level_chart.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/severity_level_panel/severity_level_chart.tsx @@ -7,12 +7,18 @@ import React, { useCallback, useMemo } from 'react'; import { ALERT_SEVERITY } from '@kbn/rule-data-utils'; import styled from 'styled-components'; -import { EuiFlexGroup, EuiFlexItem, EuiInMemoryTable, EuiLoadingSpinner } from '@elastic/eui'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiInMemoryTable, + EuiLoadingSpinner, + useEuiTheme, +} from '@elastic/eui'; import type { SeverityBuckets as SeverityData } from '../../../../overview/components/detection_response/alerts_by_status/types'; import type { FillColor } from '../../../../common/components/charts/donutchart'; import { DonutChart } from '../../../../common/components/charts/donutchart'; import { ChartLabel } from '../../../../overview/components/detection_response/alerts_by_status/chart_label'; -import { getSeverityTableColumns } from './columns'; +import { useGetSeverityTableColumns } from './columns'; import { getSeverityColor } from './helpers'; import { TOTAL_COUNT_OF_ALERTS } from '../../alerts_table/translations'; @@ -32,7 +38,8 @@ export const SeverityLevelChart: React.FC = ({ isLoading, addFilter, }) => { - const columns = useMemo(() => getSeverityTableColumns(), []); + const { euiTheme } = useEuiTheme(); + const columns = useGetSeverityTableColumns(); const count = useMemo(() => { return data @@ -42,9 +49,10 @@ export const SeverityLevelChart: React.FC = ({ : 0; }, [data]); - const fillColor: FillColor = useCallback((dataName: string) => { - return getSeverityColor(dataName); - }, []); + const fillColor: FillColor = useCallback( + (dataName: string) => getSeverityColor(dataName, euiTheme), + [euiTheme] + ); const onDonutPartitionClicked = useCallback( (level: string) => { diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.test.tsx index 2152d10115b2d..39cf9bbd8a915 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.test.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.test.tsx @@ -148,7 +148,11 @@ describe('rule helpers', () => { references: ['www.test.co'], riskScore: { value: 21, mapping: [], isMappingChecked: false }, ruleNameOverride: 'message', - severity: { value: 'low', mapping: fillEmptySeverityMappings([]), isMappingChecked: false }, + severity: { + value: 'low', + mapping: fillEmptySeverityMappings([]), + isMappingChecked: false, + }, tags: ['tag1', 'tag2'], threat: getThreatMock(), timestampOverride: 'event.ingested', diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx index 1dff767667450..f9edd66e7fb20 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx @@ -43,7 +43,7 @@ import type { ActionsStepRule, } from './types'; import { DataSourceType, AlertSuppressionDurationType } from './types'; -import { severityOptions } from '../../../../detection_engine/rule_creation_ui/components/step_about_rule/data'; +import { SeverityLevel } from '../../../../detection_engine/rule_creation_ui/components/step_about_rule/data'; import { DEFAULT_SUPPRESSION_MISSING_FIELDS_STRATEGY } from '../../../../../common/detection_engine/constants'; import type { RuleAction, RuleResponse } from '../../../../../common/api/detection_engine'; import { normalizeMachineLearningJobId } from '../../../../common/utils/normalize_machine_learning_job_id'; @@ -272,11 +272,14 @@ const severitySortMapping = { }; export const fillEmptySeverityMappings = (mappings: SeverityMapping): SeverityMapping => { - const missingMappings: SeverityMapping = severityOptions.flatMap((so) => - mappings.find((mapping) => mapping.severity === so.value) == null - ? [{ field: '', value: '', operator: 'equals', severity: so.value }] - : [] - ); + const missingMappings: SeverityMapping = Object.values(SeverityLevel).flatMap((severityLevel) => { + const isSeverityLevelInMappings = mappings.some( + (mapping) => mapping.severity === severityLevel + ); + return isSeverityLevelInMappings + ? [] + : [{ field: '', value: '', operator: 'equals', severity: severityLevel }]; + }); return [...mappings, ...missingMappings].sort( (a, b) => severitySortMapping[a.severity] - severitySortMapping[b.severity] ); diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/pages/detection_engine/rules/utils.ts b/x-pack/solutions/security/plugins/security_solution/public/detections/pages/detection_engine/rules/utils.ts index f0529e22114a7..1a8bbeef18fc6 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/pages/detection_engine/rules/utils.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/pages/detection_engine/rules/utils.ts @@ -85,7 +85,11 @@ export const stepAboutDefaultValue: AboutStepRule = { description: '', isAssociatedToEndpointList: false, isBuildingBlock: false, - severity: { value: 'low', mapping: fillEmptySeverityMappings([]), isMappingChecked: false }, + severity: { + value: 'low', + mapping: fillEmptySeverityMappings([]), + isMappingChecked: false, + }, riskScore: { value: 21, mapping: [], isMappingChecked: false }, investigationFields: [], references: [''], diff --git a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/components/alert_count_insight.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/components/alert_count_insight.test.tsx index 711d441c8ba39..7fa3cbe7a2dd6 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/components/alert_count_insight.test.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/components/alert_count_insight.test.tsx @@ -6,12 +6,12 @@ */ import React from 'react'; -import { render } from '@testing-library/react'; +import { render, renderHook } from '@testing-library/react'; import { TestProviders } from '../../../../common/mock'; import { AlertCountInsight, getFormattedAlertStats } from './alert_count_insight'; import { useAlertsByStatus } from '../../../../overview/components/detection_response/alerts_by_status/use_alerts_by_status'; import type { ParsedAlertsData } from '../../../../overview/components/detection_response/alerts_by_status/types'; -import { SEVERITY_COLOR } from '../../../../overview/components/detection_response/utils'; +import { useEuiTheme } from '@elastic/eui'; import { INSIGHTS_ALERTS_COUNT_INVESTIGATE_IN_TIMELINE_BUTTON_TEST_ID, INSIGHTS_ALERTS_COUNT_TEXT_TEST_ID, @@ -145,26 +145,32 @@ describe('AlertCountInsight', () => { }); describe('getFormattedAlertStats', () => { + const { result } = renderHook(() => useEuiTheme()); + const euiTheme = result.current.euiTheme; + it('should return alert stats', () => { - const alertStats = getFormattedAlertStats(mockAlertData); + const alertStats = getFormattedAlertStats(mockAlertData, euiTheme); expect(alertStats).toEqual([ - { key: 'High', count: 2, color: SEVERITY_COLOR.high }, - { key: 'Low', count: 2, color: SEVERITY_COLOR.low }, - { key: 'Medium', count: 2, color: SEVERITY_COLOR.medium }, - { key: 'Critical', count: 2, color: SEVERITY_COLOR.critical }, + { key: 'High', count: 2, color: '#ff7e62' }, + { key: 'Low', count: 2, color: '#54b399' }, + { key: 'Medium', count: 2, color: '#f1d86f' }, + { key: 'Critical', count: 2, color: '#bd271e' }, ]); }); it('should return empty array if no active alerts are available', () => { - const alertStats = getFormattedAlertStats({ - closed: { - total: 2, - severities: [ - { key: 'high', value: 1, label: 'High' }, - { key: 'low', value: 1, label: 'Low' }, - ], + const alertStats = getFormattedAlertStats( + { + closed: { + total: 2, + severities: [ + { key: 'high', value: 1, label: 'High' }, + { key: 'low', value: 1, label: 'Low' }, + ], + }, }, - }); + euiTheme + ); expect(alertStats).toEqual([]); }); }); diff --git a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/components/alert_count_insight.tsx b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/components/alert_count_insight.tsx index 77a06132e463c..58c6e78b74bda 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/components/alert_count_insight.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/flyout/document_details/shared/components/alert_count_insight.tsx @@ -7,7 +7,14 @@ import React, { useMemo } from 'react'; import { capitalize } from 'lodash'; -import { EuiLoadingSpinner, EuiFlexItem, EuiText, type EuiFlexGroupProps } from '@elastic/eui'; +import type { EuiThemeComputed } from '@elastic/eui'; +import { + EuiLoadingSpinner, + EuiFlexItem, + EuiText, + type EuiFlexGroupProps, + useEuiTheme, +} from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { InsightDistributionBar } from './insight_distribution_bar'; import { getSeverityColor } from '../../../../detections/components/alerts_kpis/severity_level_panel/helpers'; @@ -54,7 +61,10 @@ interface AlertCountInsightProps { /** * Filters closed alerts and format the alert stats for the distribution bar */ -export const getFormattedAlertStats = (alertsData: ParsedAlertsData) => { +export const getFormattedAlertStats = ( + alertsData: ParsedAlertsData, + euiTheme: EuiThemeComputed +) => { const severityMap = new Map(); const filteredAlertsData: ParsedAlertsData = alertsData @@ -73,7 +83,7 @@ export const getFormattedAlertStats = (alertsData: ParsedAlertsData) => { const alertStats = Array.from(severityMap, ([key, count]) => ({ key: capitalize(key), count, - color: getSeverityColor(key), + color: getSeverityColor(key, euiTheme), })); return alertStats; }; @@ -87,6 +97,7 @@ export const AlertCountInsight: React.FC = ({ direction, 'data-test-subj': dataTestSubj, }) => { + const { euiTheme } = useEuiTheme(); const { timelinePrivileges: { read: canUseTimeline }, } = useUserPrivileges(); @@ -103,7 +114,7 @@ export const AlertCountInsight: React.FC = ({ from, }); - const alertStats = useMemo(() => getFormattedAlertStats(items), [items]); + const alertStats = useMemo(() => getFormattedAlertStats(items, euiTheme), [items, euiTheme]); const totalAlertCount = useMemo( () => alertStats.reduce((acc, item) => acc + item.count, 0), diff --git a/x-pack/solutions/security/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/alerts_by_status.tsx b/x-pack/solutions/security/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/alerts_by_status.tsx index 7f2dd03ad8079..b8df2ea6df517 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/alerts_by_status.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/alerts_by_status.tsx @@ -5,6 +5,7 @@ * 2.0. */ +import type { EuiThemeComputed } from '@elastic/eui'; import { EuiFlexGroup, EuiFlexItem, @@ -12,11 +13,11 @@ import { EuiProgress, EuiSpacer, EuiText, + useEuiTheme, useIsWithinMaxBreakpoint, useIsWithinMinBreakpoint, } from '@elastic/eui'; import React, { useCallback, useMemo } from 'react'; -import type { Severity } from '@kbn/securitysolution-io-ts-alerting-types'; import styled from 'styled-components'; import { ALERT_WORKFLOW_STATUS, ALERT_SEVERITY } from '@kbn/rule-data-utils'; @@ -49,12 +50,10 @@ import { } from '../translations'; import { useQueryToggle } from '../../../../common/containers/query_toggle'; import { VIEW_ALERTS } from '../../../pages/translations'; -import { SEVERITY_COLOR } from '../utils'; import { FormattedCount } from '../../../../common/components/formatted_number'; import { useUserPrivileges } from '../../../../common/components/user_privileges'; import { ChartLabel } from './chart_label'; import { Legend } from '../../../../common/components/charts/legend'; -import { emptyDonutColor } from '../../../../common/components/charts/donutchart_empty'; import { LastUpdatedAt } from '../../../../common/components/last_updated_at'; import { LinkButton, useGetSecuritySolutionLinkProps } from '../../../../common/components/links'; import { useNavigateToTimeline } from '../hooks/use_navigate_to_timeline'; @@ -66,6 +65,7 @@ import { SourcererScopeName } from '../../../../sourcerer/store/model'; import { VisualizationEmbeddable } from '../../../../common/components/visualization_actions/visualization_embeddable'; import type { Status } from '../../../../../common/api/detection_engine'; import { getAlertsByStatusAttributes } from '../../../../common/components/visualization_actions/lens_attributes/common/alerts/alerts_by_status_donut'; +import { getRiskSeverityColors } from '../../../../common/utils/risk_color_palette'; const StyledFlexItem = styled(EuiFlexItem)` padding: 0 4px; @@ -85,12 +85,20 @@ interface AlertsByStatusProps { signalIndexName: string | null; } -const chartConfigs: Array<{ key: Severity; label: string; color: string }> = [ - { key: 'critical', label: STATUS_CRITICAL_LABEL, color: SEVERITY_COLOR.critical }, - { key: 'high', label: STATUS_HIGH_LABEL, color: SEVERITY_COLOR.high }, - { key: 'medium', label: STATUS_MEDIUM_LABEL, color: SEVERITY_COLOR.medium }, - { key: 'low', label: STATUS_LOW_LABEL, color: SEVERITY_COLOR.low }, -]; +const getChartConfigs = (euiTheme: EuiThemeComputed) => { + const palette = getRiskSeverityColors(euiTheme); + + return [ + { key: 'critical', label: STATUS_CRITICAL_LABEL, color: palette.critical }, + { key: 'high', label: STATUS_HIGH_LABEL, color: palette.high }, + { key: 'medium', label: STATUS_MEDIUM_LABEL, color: palette.medium }, + { key: 'low', label: STATUS_LOW_LABEL, color: palette.low }, + ].map((config) => ({ + ...config, + field: ALERT_SEVERITY, + value: config.label, + })); +}; const eventKindSignalFilter: EntityFilter = { field: 'event.kind', @@ -107,6 +115,7 @@ export const AlertsByStatus = ({ signalIndexName, entityFilter, }: AlertsByStatusProps) => { + const { euiTheme } = useEuiTheme(); const { toggleStatus, setToggleStatus } = useQueryToggle(DETECTION_RESPONSE_ALERTS_BY_STATUS_ID); const { openTimelineWithFilters } = useNavigateToTimeline(); const navigateToAlerts = useNavigateToAlertsPageWithFilters(); @@ -154,15 +163,7 @@ export const AlertsByStatus = ({ to, from, }); - const legendItems: LegendItem[] = useMemo( - () => - chartConfigs.map((d) => ({ - color: d.color, - field: ALERT_SEVERITY, - value: d.label, - })), - [] - ); + const legendItems: LegendItem[] = useMemo(() => getChartConfigs(euiTheme), [euiTheme]); const navigateToAlertsWithStatus = useCallback( (status: Status, level?: string) => @@ -219,9 +220,11 @@ export const AlertsByStatus = ({ const totalAlertsCount = isDonutChartEmbeddablesEnabled ? visualizationTotalAlerts : totalAlerts; - const fillColor: FillColor = useCallback((dataName: string) => { - return chartConfigs.find((cfg) => cfg.label === dataName)?.color ?? emptyDonutColor; - }, []); + const fillColor: FillColor = useCallback( + (dataName: string) => + legendItems.find(({ value }) => value === dataName)?.color ?? euiTheme.colors.textSubdued, + [euiTheme.colors.textSubdued, legendItems] + ); return ( <> diff --git a/x-pack/solutions/security/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/host_alerts_table.tsx b/x-pack/solutions/security/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/host_alerts_table.tsx index 3d4070d4cd8e3..b4e3bf1072eb2 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/host_alerts_table.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/overview/components/detection_response/host_alerts_table/host_alerts_table.tsx @@ -30,7 +30,7 @@ import { HostDetailsLink } from '../../../../common/components/links'; import { useQueryToggle } from '../../../../common/containers/query_toggle'; import * as i18n from '../translations'; -import { ITEMS_PER_PAGE, SEVERITY_COLOR } from '../utils'; +import { ITEMS_PER_PAGE } from '../utils'; import type { HostAlertsItem } from './use_host_alerts_items'; import { useHostAlertsItems } from './use_host_alerts_items'; import { @@ -40,6 +40,7 @@ import { } from '../../../../common/components/cell_actions'; import { useGlobalFilterQuery } from '../../../../common/hooks/use_global_filter_query'; import { SourcererScopeName } from '../../../../sourcerer/store/model'; +import { useRiskSeverityColors } from '../../../../common/utils/risk_color_palette'; interface HostAlertsTableProps { signalIndexName: string | null; @@ -87,7 +88,7 @@ export const HostAlertsTable = React.memo(({ signalIndexName }: HostAlertsTableP filterQuery, }); - const columns = useMemo(() => getTableColumns(openHostInAlerts), [openHostInAlerts]); + const columns = useGetTableColumns(openHostInAlerts); return ( @@ -132,54 +133,29 @@ export const HostAlertsTable = React.memo(({ signalIndexName }: HostAlertsTableP HostAlertsTable.displayName = 'HostAlertsTable'; -const getTableColumns: GetTableColumns = (handleClick) => [ - { - field: 'hostName', - name: i18n.HOST_ALERTS_HOSTNAME_COLUMN, - 'data-test-subj': 'hostSeverityAlertsTable-hostName', - render: (hostName: string) => ( - - - - ), - }, - { - field: 'totalAlerts', - name: i18n.ALERTS_TEXT, - 'data-test-subj': 'hostSeverityAlertsTable-totalAlerts', - render: (totalAlerts: number, { hostName }) => ( - - handleClick({ hostName })} - > - - - - ), - }, - { - field: 'critical', - name: i18n.STATUS_CRITICAL_LABEL, - render: (count: number, { hostName }) => ( - - {count > 0 ? ( +const useGetTableColumns: GetTableColumns = (handleClick) => { + const severityColors = useRiskSeverityColors(); + return useMemo( + () => [ + { + field: 'hostName', + name: i18n.HOST_ALERTS_HOSTNAME_COLUMN, + 'data-test-subj': 'hostSeverityAlertsTable-hostName', + render: (hostName: string) => ( + + + + ), + }, + { + field: 'totalAlerts', + name: i18n.ALERTS_TEXT, + 'data-test-subj': 'hostSeverityAlertsTable-totalAlerts', + render: (totalAlerts: number, { hostName }) => ( [ triggerId={SecurityCellActionsTrigger.ALERTS_COUNT} sourcererScopeId={SourcererScopeName.detections} metadata={{ - andFilters: [ - { field: 'kibana.alert.severity', value: 'critical' }, - { field: 'kibana.alert.workflow_status', value: 'open' }, - ], + andFilters: [{ field: 'kibana.alert.workflow_status', value: 'open' }], }} > handleClick({ hostName, severity: 'critical' })} + data-test-subj="hostSeverityAlertsTable-totalAlertsLink" + disabled={totalAlerts === 0} + onClick={() => handleClick({ hostName })} > - + - ) : ( - - )} - - ), - }, - { - field: 'high', - name: i18n.STATUS_HIGH_LABEL, - render: (count: number, { hostName }) => ( - - {count > 0 ? ( - ( + - handleClick({ hostName, severity: 'high' })}> + {count > 0 ? ( + + handleClick({ hostName, severity: 'critical' })} + > + + + + ) : ( - - - ) : ( - - )} - - ), - }, - { - field: 'medium', - name: i18n.STATUS_MEDIUM_LABEL, - render: (count: number, { hostName }) => ( - - {count > 0 ? ( - - handleClick({ hostName, severity: 'medium' })}> + )} + + ), + }, + { + field: 'high', + name: i18n.STATUS_HIGH_LABEL, + render: (count: number, { hostName }) => ( + + {count > 0 ? ( + + handleClick({ hostName, severity: 'high' })}> + + + + ) : ( - - - ) : ( - - )} - - ), - }, - { - field: 'low', - name: i18n.STATUS_LOW_LABEL, - render: (count: number, { hostName }) => ( - - {count > 0 ? ( - - handleClick({ hostName, severity: 'low' })}> + )} + + ), + }, + { + field: 'medium', + name: i18n.STATUS_MEDIUM_LABEL, + render: (count: number, { hostName }) => ( + + {count > 0 ? ( + + handleClick({ hostName, severity: 'medium' })}> + + + + ) : ( - - - ) : ( - - )} - - ), - }, -]; + )} + + ), + }, + { + field: 'low', + name: i18n.STATUS_LOW_LABEL, + render: (count: number, { hostName }) => ( + + {count > 0 ? ( + + handleClick({ hostName, severity: 'low' })}> + + + + ) : ( + + )} + + ), + }, + ], + [ + handleClick, + severityColors.critical, + severityColors.high, + severityColors.low, + severityColors.medium, + ] + ); +}; diff --git a/x-pack/solutions/security/plugins/security_solution/public/overview/components/detection_response/rule_alerts_table/rule_alerts_table.tsx b/x-pack/solutions/security/plugins/security_solution/public/overview/components/detection_response/rule_alerts_table/rule_alerts_table.tsx index 6c88a6e903ed7..6938c389d4744 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/overview/components/detection_response/rule_alerts_table/rule_alerts_table.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/overview/components/detection_response/rule_alerts_table/rule_alerts_table.tsx @@ -25,7 +25,6 @@ import { SecurityCellActionsTrigger } from '../../../../app/actions/constants'; import { useNavigateToAlertsPageWithFilters } from '../../../../common/hooks/use_navigate_to_alerts_page_with_filters'; import { HeaderSection } from '../../../../common/components/header_section'; -import { SEVERITY_COLOR } from '../utils'; import * as i18n from '../translations'; import type { RuleAlertsItem } from './use_rule_alerts_items'; import { useRuleAlertsItems } from './use_rule_alerts_items'; @@ -40,6 +39,7 @@ import { FormattedCount } from '../../../../common/components/formatted_number'; import { SecurityCellActions, CellActionsMode } from '../../../../common/components/cell_actions'; import { useGlobalFilterQuery } from '../../../../common/hooks/use_global_filter_query'; import { SourcererScopeName } from '../../../../sourcerer/store/model'; +import { useRiskSeverityColors } from '../../../../common/utils/risk_color_palette'; export interface RuleAlertsTableProps { signalIndexName: string | null; @@ -54,82 +54,88 @@ export type GetTableColumns = (params: { const DETECTION_RESPONSE_RULE_ALERTS_QUERY_ID = 'detection-response-rule-alerts-severity-table' as const; -export const getTableColumns: GetTableColumns = ({ +export const useGetTableColumns: GetTableColumns = ({ getAppUrl, navigateTo, openRuleInAlertsPage, -}) => [ - { - field: 'name', - name: i18n.RULE_ALERTS_COLUMN_RULE_NAME, - render: (name: string, { id }) => { - const url = getAppUrl({ deepLinkId: SecurityPageName.rules, path: `id/${id}` }); - return ( - - {/* eslint-disable-next-line @elastic/eui/href-or-on-click */} - { - if (ev) { - ev.preventDefault(); - } - navigateTo({ url }); +}) => { + const severityColors = useRiskSeverityColors(); + return useMemo( + () => [ + { + field: 'name', + name: i18n.RULE_ALERTS_COLUMN_RULE_NAME, + render: (name: string, { id }) => { + const url = getAppUrl({ deepLinkId: SecurityPageName.rules, path: `id/${id}` }); + return ( + + {/* eslint-disable-next-line @elastic/eui/href-or-on-click */} + { + if (ev) { + ev.preventDefault(); + } + navigateTo({ url }); + }} + > + {name} + + + ); + }, + }, + { + field: 'last_alert_at', + name: i18n.RULE_ALERTS_COLUMN_LAST_ALERT, + 'data-test-subj': 'severityRuleAlertsTable-lastAlertAt', + render: (lastAlertAt: string) => , + }, + { + field: 'alert_count', + name: i18n.RULE_ALERTS_COLUMN_ALERT_COUNT, + 'data-test-subj': 'severityRuleAlertsTable-alertCount', + render: (alertCount: number, { name }) => ( + - {name} - - - ); - }, - }, - { - field: 'last_alert_at', - name: i18n.RULE_ALERTS_COLUMN_LAST_ALERT, - 'data-test-subj': 'severityRuleAlertsTable-lastAlertAt', - render: (lastAlertAt: string) => , - }, - { - field: 'alert_count', - name: i18n.RULE_ALERTS_COLUMN_ALERT_COUNT, - 'data-test-subj': 'severityRuleAlertsTable-alertCount', - render: (alertCount: number, { name }) => ( - - openRuleInAlertsPage(name)} - > - - - - ), - }, - { - field: 'severity', - name: i18n.RULE_ALERTS_COLUMN_SEVERITY, - 'data-test-subj': 'severityRuleAlertsTable-severity', - render: (severity: Severity) => ( - {capitalize(severity)} - ), - }, -]; + openRuleInAlertsPage(name)} + > + + + + ), + }, + { + field: 'severity', + name: i18n.RULE_ALERTS_COLUMN_SEVERITY, + 'data-test-subj': 'severityRuleAlertsTable-severity', + render: (severity: Severity) => ( + {capitalize(severity)} + ), + }, + ], + [getAppUrl, navigateTo, openRuleInAlertsPage, severityColors] + ); +}; export const RuleAlertsTable = React.memo(({ signalIndexName }) => { const { getAppUrl, navigateTo } = useNavigation(); @@ -163,10 +169,7 @@ export const RuleAlertsTable = React.memo(({ signalIndexNa }); }, [openAlertsPageWithFilter]); - const columns = useMemo( - () => getTableColumns({ getAppUrl, navigateTo, openRuleInAlertsPage }), - [getAppUrl, navigateTo, openRuleInAlertsPage] - ); + const columns = useGetTableColumns({ getAppUrl, navigateTo, openRuleInAlertsPage }); return ( diff --git a/x-pack/solutions/security/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/user_alerts_table.tsx b/x-pack/solutions/security/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/user_alerts_table.tsx index d2ec3efe63aa5..c4d4d0bd710e3 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/user_alerts_table.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/user_alerts_table.tsx @@ -30,12 +30,13 @@ import { LastUpdatedAt } from '../../../../common/components/last_updated_at'; import { UserDetailsLink } from '../../../../common/components/links'; import { useQueryToggle } from '../../../../common/containers/query_toggle'; import * as i18n from '../translations'; -import { ITEMS_PER_PAGE, SEVERITY_COLOR } from '../utils'; +import { ITEMS_PER_PAGE } from '../utils'; import type { UserAlertsItem } from './use_user_alerts_items'; import { useUserAlertsItems } from './use_user_alerts_items'; import { SecurityCellActions, CellActionsMode } from '../../../../common/components/cell_actions'; import { useGlobalFilterQuery } from '../../../../common/hooks/use_global_filter_query'; import { SourcererScopeName } from '../../../../sourcerer/store/model'; +import { useRiskSeverityColors } from '../../../../common/utils/risk_color_palette'; interface UserAlertsTableProps { signalIndexName: string | null; @@ -82,8 +83,7 @@ export const UserAlertsTable = React.memo(({ signalIndexName }: UserAlertsTableP signalIndexName, filterQuery, }); - - const columns = useMemo(() => getTableColumns(openUserInAlerts), [openUserInAlerts]); + const columns = useGetTableColumns(openUserInAlerts); return ( @@ -128,54 +128,29 @@ export const UserAlertsTable = React.memo(({ signalIndexName }: UserAlertsTableP UserAlertsTable.displayName = 'UserAlertsTable'; -const getTableColumns: GetTableColumns = (handleClick) => [ - { - field: 'userName', - name: i18n.USER_ALERTS_USERNAME_COLUMN, - 'data-test-subj': 'userSeverityAlertsTable-userName', - render: (userName: string) => ( - - - - ), - }, - { - field: 'totalAlerts', - name: i18n.ALERTS_TEXT, - 'data-test-subj': 'userSeverityAlertsTable-totalAlerts', - render: (totalAlerts: number, { userName }) => ( - - handleClick({ userName })} - > - - - - ), - }, - { - field: 'critical', - name: i18n.STATUS_CRITICAL_LABEL, - render: (count: number, { userName }) => ( - - {count > 0 ? ( +const useGetTableColumns: GetTableColumns = (handleClick) => { + const severityColors = useRiskSeverityColors(); + return useMemo( + () => [ + { + field: 'userName', + name: i18n.USER_ALERTS_USERNAME_COLUMN, + 'data-test-subj': 'userSeverityAlertsTable-userName', + render: (userName: string) => ( + + + + ), + }, + { + field: 'totalAlerts', + name: i18n.ALERTS_TEXT, + 'data-test-subj': 'userSeverityAlertsTable-totalAlerts', + render: (totalAlerts: number, { userName }) => ( [ triggerId={SecurityCellActionsTrigger.ALERTS_COUNT} sourcererScopeId={SourcererScopeName.detections} metadata={{ - andFilters: [ - { field: 'kibana.alert.severity', value: 'critical' }, - { field: 'kibana.alert.workflow_status', value: 'open' }, - ], + andFilters: [{ field: 'kibana.alert.workflow_status', value: 'open' }], }} > handleClick({ userName, severity: 'critical' })} + data-test-subj="userSeverityAlertsTable-totalAlertsLink" + disabled={totalAlerts === 0} + onClick={() => handleClick({ userName })} > - + - ) : ( - - )} - - ), - }, - { - field: 'high', - name: i18n.STATUS_HIGH_LABEL, - render: (count: number, { userName }) => ( - - {count > 0 ? ( - ( + - handleClick({ userName, severity: 'high' })}> + {count > 0 ? ( + + handleClick({ userName, severity: 'critical' })} + > + + + + ) : ( - - - ) : ( - - )} - - ), - }, - { - field: 'medium', - name: i18n.STATUS_MEDIUM_LABEL, - render: (count: number, { userName }) => ( - - {count > 0 ? ( - - handleClick({ userName, severity: 'medium' })}> + )} + + ), + }, + { + field: 'high', + name: i18n.STATUS_HIGH_LABEL, + render: (count: number, { userName }) => ( + + {count > 0 ? ( + + handleClick({ userName, severity: 'high' })}> + + + + ) : ( - - - ) : ( - - )} - - ), - }, - { - field: 'low', - name: i18n.STATUS_LOW_LABEL, - render: (count: number, { userName }) => ( - - {count > 0 ? ( - - handleClick({ userName, severity: 'low' })}> + )} + + ), + }, + { + field: 'medium', + name: i18n.STATUS_MEDIUM_LABEL, + render: (count: number, { userName }) => ( + + {count > 0 ? ( + + handleClick({ userName, severity: 'medium' })}> + + + + ) : ( - - - ) : ( - - )} - - ), - }, -]; + )} + + ), + }, + { + field: 'low', + name: i18n.STATUS_LOW_LABEL, + render: (count: number, { userName }) => ( + + {count > 0 ? ( + + handleClick({ userName, severity: 'low' })}> + + + + ) : ( + + )} + + ), + }, + ], + [handleClick, severityColors] + ); +}; diff --git a/x-pack/solutions/security/plugins/security_solution/public/overview/components/detection_response/utils.tsx b/x-pack/solutions/security/plugins/security_solution/public/overview/components/detection_response/utils.tsx index 61ed89aabf0f0..e272ca6530053 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/overview/components/detection_response/utils.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/overview/components/detection_response/utils.tsx @@ -4,7 +4,9 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -// @deprecated +/** + * @deprecated, use getRiskSeverityColors or useRiskSeverityColors from /common/utils/risk_color_palette.tsx instead + */ export const SEVERITY_COLOR = { critical: '#E7664C', high: '#DA8B45',