Skip to content

Commit

Permalink
[Entity Analytics] Show Asset Criticality in Risk Input Left Flyout (e…
Browse files Browse the repository at this point in the history
…lastic#174638)

## Summary

Show the asset criticality as part of the risk inputs under a new
contexts section:
<img width="1517" alt="Screenshot 2024-01-10 at 16 45 29"
src="https://github.com/elastic/kibana/assets/3315046/4ab7fd16-2849-4d9c-8f1c-f9cd9b677e8f">

If there is no criticality assigned here is what it looks like:
<img width="1098" alt="Screenshot 2024-01-10 at 12 05 28"
src="https://github.com/elastic/kibana/assets/3315046/817e4397-1a3f-4e65-be27-dbadb364e693">

this is based off the criticality_level on the risk score document not
the current asset criticality of the entity.

## Test steps

Assign asset criticality to a host or user and raise alerts + a risk
score for them, view the host details flyout and then expand risk
inputs, the asset criticality at the time of the score should be shown.
  • Loading branch information
hop-dev authored and CoenWarmer committed Feb 15, 2024
1 parent f4ddc9c commit fa10460
Show file tree
Hide file tree
Showing 30 changed files with 446 additions and 130 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
* 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 { AssetCriticalityRecord } from '../../api/entity_analytics/asset_criticality';

export type CriticalityLevel = AssetCriticalityRecord['criticality_level'];
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* 2.0.
*/

import type { CriticalityLevel } from '../asset_criticality/types';
import type { RiskCategories } from './risk_weights/types';

export enum RiskScoreEntity {
Expand Down Expand Up @@ -56,9 +57,9 @@ export interface RiskScore {
'@timestamp': string;
id_field: string;
id_value: string;
criticality_level?: string | undefined;
criticality_level?: CriticalityLevel;
criticality_modifier?: number | undefined;
calculated_level: string;
calculated_level: RiskLevels;
calculated_score: number;
calculated_score_norm: number;
category_1_score: number;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,8 @@
import type { IEsSearchResponse } from '@kbn/data-plugin/common';

import type { Inspect, Maybe, SortField } from '../../../common';
import {
type RiskInputs,
RiskLevels as RiskSeverity,
} from '../../../../entity_analytics/risk_engine';
import type { RiskScore } from '../../../../entity_analytics/risk_engine';
import { RiskLevels as RiskSeverity } from '../../../../entity_analytics/risk_engine';

export interface HostsRiskScoreStrategyResponse extends IEsSearchResponse {
inspect?: Maybe<Inspect>;
Expand All @@ -25,16 +23,9 @@ export interface UsersRiskScoreStrategyResponse extends IEsSearchResponse {
data: UserRiskScore[] | undefined;
}

export interface RiskStats {
export interface RiskStats extends RiskScore {
rule_risks: RuleRisk[];
calculated_score_norm: number;
multipliers: string[];
calculated_level: RiskSeverity;
inputs?: RiskInputs;
category_1_score: number;
category_1_count: number;
category_2_score: number;
category_2_count: number;
}

export { RiskSeverity };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,24 @@
* 2.0.
*/
import { getAlertsQueryForRiskScore } from './get_alerts_query_for_risk_score';
import type { RiskStats } from '../../../common/search_strategy/security_solution/risk_score/all';
import { RiskSeverity } from '../../../common/search_strategy/security_solution/risk_score/all';

const risk = {
const risk: RiskStats = {
calculated_level: RiskSeverity.critical,
calculated_score_norm: 70,
rule_risks: [],
multipliers: [],
'@timestamp': '',
id_field: '',
id_value: '',
calculated_score: 0,
category_1_score: 0,
category_1_count: 0,
category_2_score: 0,
category_2_count: 0,
notes: [],
inputs: [],
};

describe('getAlertsQueryForRiskScore', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import React from 'react';
import { EuiHealth, EuiText } from '@elastic/eui';
import { euiLightVars } from '@kbn/ui-theme';
import { FormattedMessage } from '@kbn/i18n-react';
import { CRITICALITY_LEVEL_TITLE, CRITICALITY_LEVEL_DESCRIPTION } from './translations';
import type { CriticalityLevel } from '../../../../common/entity_analytics/asset_criticality/types';

const CRITICALITY_LEVEL_COLOR: Record<CriticalityLevel, string> = {
very_important: '#E7664C',
important: '#D6BF57',
normal: '#54B399',
not_important: euiLightVars.euiColorMediumShade,
};

export const AssetCriticalityBadge: React.FC<{
criticalityLevel: CriticalityLevel;
withDescription?: boolean;
style?: React.CSSProperties;
dataTestSubj?: string;
}> = ({
criticalityLevel,
style,
dataTestSubj = 'asset-criticality-badge',
withDescription = false,
}) => {
const showDescription = withDescription ?? false;
const badgeContent = showDescription ? (
<>
<strong>{CRITICALITY_LEVEL_TITLE[criticalityLevel]}</strong>
<EuiText size="s" color="subdued">
<p>{CRITICALITY_LEVEL_DESCRIPTION[criticalityLevel]}</p>
</EuiText>
</>
) : (
CRITICALITY_LEVEL_TITLE[criticalityLevel]
);

return (
<EuiHealth
data-test-subj={dataTestSubj}
color={CRITICALITY_LEVEL_COLOR[criticalityLevel]}
style={style}
>
{badgeContent}
</EuiHealth>
);
};

export const AssetCriticalityBadgeAllowMissing: React.FC<{
criticalityLevel?: CriticalityLevel;
withDescription?: boolean;
style?: React.CSSProperties;
dataTestSubj?: string;
}> = ({ criticalityLevel, style, dataTestSubj, withDescription }) => {
if (criticalityLevel) {
return (
<AssetCriticalityBadge
criticalityLevel={criticalityLevel}
dataTestSubj={dataTestSubj}
withDescription={withDescription}
style={style}
/>
);
}

return (
<EuiHealth color="subdued" data-test-subj={dataTestSubj}>
<FormattedMessage
id="xpack.securitySolution.entityAnalytics.assetCriticality.noCriticality"
defaultMessage="No criticality assigned"
/>
</EuiHealth>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,12 @@
*/

import type { EuiSuperSelectOption } from '@elastic/eui';

import {
EuiAccordion,
EuiButton,
EuiButtonEmpty,
EuiFlexGroup,
EuiFlexItem,
EuiHealth,
EuiLoadingSpinner,
EuiModal,
EuiModalBody,
Expand All @@ -26,21 +24,17 @@ import {
EuiHorizontalRule,
useEuiTheme,
} from '@elastic/eui';

import React, { useState } from 'react';

import { FormattedMessage } from '@kbn/i18n-react';

import { css } from '@emotion/react';
import { PICK_ASSET_CRITICALITY } from './translations';
import {
CRITICALITY_LEVEL_DESCRIPTION,
CRITICALITY_LEVEL_TITLE,
PICK_ASSET_CRITICALITY,
} from './translations';
AssetCriticalityBadge,
AssetCriticalityBadgeAllowMissing,
} from './asset_criticality_badge';
import type { Entity, ModalState, State } from './use_asset_criticality';
import { useAssetCriticalityData, useCriticalityModal } from './use_asset_criticality';
import type { CriticalityLevel } from './common';
import { CRITICALITY_LEVEL_COLOR } from './common';
import type { CriticalityLevel } from '../../../../common/entity_analytics/asset_criticality/types';

interface Props {
entity: Entity;
Expand Down Expand Up @@ -86,20 +80,11 @@ export const AssetCriticalitySelector: React.FC<Props> = ({ entity }) => {
>
<EuiFlexItem>
<EuiText size="s">
{criticality.status === 'update' && criticality.query.data?.criticality_level ? (
<EuiHealth
data-test-subj="asset-criticality-level"
color={CRITICALITY_LEVEL_COLOR[criticality.query.data.criticality_level]}
>
{CRITICALITY_LEVEL_TITLE[criticality.query.data.criticality_level]}
</EuiHealth>
) : (
<EuiHealth color="subdued">
<FormattedMessage
id="xpack.securitySolution.entityAnalytics.assetCriticality.noCriticality"
defaultMessage="No criticality assigned yet"
/>
</EuiHealth>
{criticality.status === 'update' && (
<AssetCriticalityBadgeAllowMissing
criticalityLevel={criticality.query.data?.criticality_level}
dataTestSubj="asset-criticality-level"
/>
)}
</EuiText>
</EuiFlexItem>
Expand Down Expand Up @@ -194,21 +179,15 @@ const AssetCriticalityModal: React.FC<ModalProps> = ({ criticality, modal, entit
const option = (level: CriticalityLevel): EuiSuperSelectOption<CriticalityLevel> => ({
value: level,
dropdownDisplay: (
<EuiHealth
color={CRITICALITY_LEVEL_COLOR[level]}
<AssetCriticalityBadge
criticalityLevel={level}
style={{ lineHeight: 'inherit' }}
data-test-subj="asset-criticality-modal-select-option"
>
<strong>{CRITICALITY_LEVEL_TITLE[level]}</strong>
<EuiText size="s" color="subdued">
<p>{CRITICALITY_LEVEL_DESCRIPTION[level]}</p>
</EuiText>
</EuiHealth>
dataTestSubj="asset-criticality-modal-select-option"
withDescription
/>
),
inputDisplay: (
<EuiHealth color={CRITICALITY_LEVEL_COLOR[level]} style={{ lineHeight: 'inherit' }}>
{CRITICALITY_LEVEL_TITLE[level]}
</EuiHealth>
<AssetCriticalityBadge criticalityLevel={level} style={{ lineHeight: 'inherit' }} />
),
});
const options: Array<EuiSuperSelectOption<CriticalityLevel>> = [
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

export * from './asset_criticality_badge';
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*/

import { i18n } from '@kbn/i18n';
import type { CriticalityLevel } from './common';
import type { CriticalityLevel } from '../../../../common/entity_analytics/asset_criticality/types';

export const PICK_ASSET_CRITICALITY = i18n.translate(
'xpack.securitySolution.entityAnalytics.assetCriticality.pickerText',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,16 @@ import type { AssetCriticalityRecord } from '../../../../common/api/entity_analy
import type { EntityAnalyticsPrivileges } from '../../../../common/api/entity_analytics/common';
import type { AssetCriticality } from '../../api/api';
import { useEntityAnalyticsRoutes } from '../../api/api';
import { buildCriticalityQueryKeys } from './common';

// SUGGESTION: @tiansivive Move this to some more general place within Entity Analytics
export const buildCriticalityQueryKeys = (id: string) => {
const ASSET_CRITICALITY = 'ASSET_CRITICALITY';
const PRIVILEGES = 'PRIVILEGES';
return {
doc: [ASSET_CRITICALITY, id],
privileges: [ASSET_CRITICALITY, PRIVILEGES, id],
};
};

export const useAssetCriticalityData = (entity: Entity, modal: ModalState): State => {
const QC = useQueryClient();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import { EuiButtonIcon, EuiContextMenu, EuiPopover } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React, { useCallback, useMemo, useState } from 'react';
import type { AlertRawData } from '../tabs/risk_inputs';
import type { AlertRawData } from '../tabs/risk_inputs/risk_inputs_tab';
import { useRiskInputActionsPanels } from '../hooks/use_risk_input_actions_panels';

interface ActionColumnProps {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
import { FormattedMessage } from '@kbn/i18n-react';
import { css } from '@emotion/react';
import { useRiskInputActionsPanels } from '../hooks/use_risk_input_actions_panels';
import type { AlertRawData } from '../tabs/risk_inputs';
import type { AlertRawData } from '../tabs/risk_inputs/risk_inputs_tab';

interface Props {
selectedAlerts: AlertRawData[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { useGlobalTime } from '../../../../common/containers/use_global_time';
import { SourcererScopeName } from '../../../../common/store/sourcerer/model';
import { useAddBulkToTimelineAction } from '../../../../detections/components/alerts_table/timeline_actions/use_add_bulk_to_timeline';
import { useKibana } from '../../../../common/lib/kibana/kibana_react';
import type { AlertRawData } from '../tabs/risk_inputs';
import type { AlertRawData } from '../tabs/risk_inputs/risk_inputs_tab';

/**
* The returned actions only support alerts risk inputs.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { i18n } from '@kbn/i18n';
import { get } from 'lodash/fp';
import { ALERT_RULE_NAME } from '@kbn/rule-data-utils';
import { useRiskInputActions } from './use_risk_input_actions';
import type { AlertRawData } from '../tabs/risk_inputs';
import type { AlertRawData } from '../tabs/risk_inputs/risk_inputs_tab';

export const useRiskInputActionsPanels = (alerts: AlertRawData[], closePopover: () => void) => {
const { cases: casesService } = useKibana<{ cases?: CasesService }>().services;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import React from 'react';
import { FormattedMessage } from '@kbn/i18n-react';
import { EntityDetailsLeftPanelTab } from '../../../flyout/entity_details/shared/components/left_panel/left_panel_header';
import { PREFIX } from '../../../flyout/shared/test_ids';
import type { RiskInputsTabProps } from './tabs/risk_inputs';
import { RiskInputsTab } from './tabs/risk_inputs';
import type { RiskInputsTabProps } from './tabs/risk_inputs/risk_inputs_tab';
import { RiskInputsTab } from './tabs/risk_inputs/risk_inputs_tab';

export const RISK_INPUTS_TAB_TEST_ID = `${PREFIX}RiskInputsTab` as const;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*/

import { ALERT_RULE_NAME, ALERT_RULE_UUID } from '@kbn/rule-data-utils';
import type { AlertRawData } from '../tabs/risk_inputs';
import type { AlertRawData } from '../tabs/risk_inputs/risk_inputs_tab';

export const alertDataMock: AlertRawData = {
_id: 'test-id',
Expand Down
Loading

0 comments on commit fa10460

Please sign in to comment.