Skip to content

Commit

Permalink
[Security Solution] ThreeWayDiff UI: Add FieldReadOnly component (#…
Browse files Browse the repository at this point in the history
…191499)

**Partially addresses: #171520
**Follow-up PR: #192342

This is the 1st of the 2 PRs for `FieldReadOnly`. The second PR will add
more field components. I split the work into two PRs to keep the number
of changed files reasonable.

## Summary

This PR adds the `FieldReadOnly` component along with some field
components. Field components display a read-only view of a particular
`DiffableRule` field, similar to how fields are shown on the Rule
Details page.

`FieldReadOnly` and field components will be displayed in the right side
of the new Diff tab of the Upgrade flyout (see it on the [Miro
board](https://miro.com/app/board/uXjVK0gqjjQ=/?moveToWidget=3458764594148126123&cot=14)).
They will let the user see how an upgraded version of a rule will look
like in a user-friendly way.


### Running
`FinalReadOnly` and its field components are not yet integrated into the
flyout, but you can view components in Storybook.
1. Run Storybook: `yarn storybook security_solution`
2. Go to `http://localhost:9001` in browser.

<img width="1062" alt="Scherm­afbeelding 2024-09-03 om 13 05 11"
src="https://github.com/user-attachments/assets/13b227d4-1321-47d9-a0a7-93868c9f4a15">

## Changes
- `FieldReadOnly` component itself was added. It shows a field component
based on a `fieldName` prop.
- Field components (like `DataSourceReadOnly`) were added. These
components mostly import and reuse components from the Rule Details
page.
- Each field component has a Storybook story. I had to mock dependencies
for some field components to make them work in Storybook.
- Rule Details page and Overview tab of the flyout now display query
language for Custom Query, Saved Query and Indicator Match rules.
Language can be either KQL or Lucene. Since language will be displayed
in the new Diff tab, it makes sense to show it in other places as well
to keep it consistent.
  • Loading branch information
nikitaindik authored Sep 9, 2024
1 parent fa686b9 commit e308d17
Show file tree
Hide file tree
Showing 39 changed files with 1,949 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export interface RuleUpgradeStatsForReview {
/** Number of installed prebuilt rules available for upgrade (stock + customized) */
num_rules_to_upgrade_total: number;

/** Number of installed prebuilt rules with upgrade conflicts (SOLVABLE or NON_SOLVALBE) */
/** Number of installed prebuilt rules with upgrade conflicts (SOLVABLE or NON_SOLVABLE) */
num_rules_with_conflicts: number;

/** Number of installed prebuilt rules with NON_SOLVABLE upgrade conflicts */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,22 @@
* 2.0.
*/

import type { RuleFieldsDiff, ThreeWayDiff } from '../../../../../common/api/detection_engine';
import { ThreeWayDiffOutcome } from '../../../../../common/api/detection_engine';
import type { Filter } from '@kbn/es-query';
import type {
DiffableAllFields,
RuleFieldsDiff,
ThreeWayDiff,
} from '../../../../../common/api/detection_engine';
import { DataSourceType, ThreeWayDiffOutcome } from '../../../../../common/api/detection_engine';
import type { FieldsGroupDiff } from '../../model/rule_details/rule_field_diff';
import {
ABOUT_UPGRADE_FIELD_ORDER,
DEFINITION_UPGRADE_FIELD_ORDER,
SCHEDULE_UPGRADE_FIELD_ORDER,
SETUP_UPGRADE_FIELD_ORDER,
} from './constants';
import * as i18n from './translations';
import { assertUnreachable } from '../../../../../common/utility_types';

export const getSectionedFieldDiffs = (fields: FieldsGroupDiff[]) => {
const aboutFields = [];
Expand Down Expand Up @@ -57,3 +64,58 @@ export const filterUnsupportedDiffOutcomes = (
);
})
);

export function getQueryLanguageLabel(language: string) {
switch (language) {
case 'kuery':
return i18n.KUERY_LANGUAGE_LABEL;
case 'lucene':
return i18n.LUCENE_LANGUAGE_LABEL;
default:
return language;
}
}

/**
* Assigns type `Filter` to items that have a `meta` property. Removes any other items.
*/
export function typeCheckFilters(filters: unknown[]): Filter[] {
return filters.filter((f) => {
if (typeof f === 'object' && f !== null && 'meta' in f) {
return true;
}

return false;
}) as Filter[];
}

type DataSourceProps =
| {
index: undefined;
dataViewId: undefined;
}
| {
index: string[];
dataViewId: undefined;
}
| {
index: undefined;
dataViewId: string;
};

/**
* Extracts `index` and `dataViewId` from a `data_source` object for use in the `Filters` component.
*/
export function getDataSourceProps(dataSource: DiffableAllFields['data_source']): DataSourceProps {
if (!dataSource) {
return { index: undefined, dataViewId: undefined };
}

if (dataSource.type === DataSourceType.index_patterns) {
return { index: dataSource.index_patterns, dataViewId: undefined };
} else if (dataSource.type === DataSourceType.data_view) {
return { index: undefined, dataViewId: dataSource.data_view_id };
}

return assertUnreachable(dataSource);
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@ const OverrideColumn = styled(EuiFlexItem)`
text-overflow: ellipsis;
`;

const OverrideValueColumn = styled(EuiFlexItem)`
width: 30px;
max-width: 30px;
const OverrideValueColumn = styled.div`
width: 50px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
`;

Expand Down Expand Up @@ -86,7 +86,7 @@ interface SeverityMappingItemProps {
severityMappingItem: SeverityMappingItemType;
}

const SeverityMappingItem = ({ severityMappingItem }: SeverityMappingItemProps) => (
export const SeverityMappingItem = ({ severityMappingItem }: SeverityMappingItemProps) => (
<EuiFlexGroup alignItems="center" gutterSize="s">
<OverrideColumn>
<EuiToolTip
Expand All @@ -96,16 +96,18 @@ const SeverityMappingItem = ({ severityMappingItem }: SeverityMappingItemProps)
<span data-test-subj="severityOverrideField">{`${severityMappingItem.field}:`}</span>
</EuiToolTip>
</OverrideColumn>
<OverrideValueColumn>
<EuiFlexItem grow={false}>
<EuiToolTip
content={severityMappingItem.value}
data-test-subj={`severityOverrideValue-${severityMappingItem.value}`}
>
<span data-test-subj="severityOverrideValue">
{defaultToEmptyTag(severityMappingItem.value)}
</span>
<OverrideValueColumn>
<span data-test-subj="severityOverrideValue">
{defaultToEmptyTag(severityMappingItem.value)}
</span>
</OverrideValueColumn>
</EuiToolTip>
</OverrideValueColumn>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiIcon type={'sortRight'} />
</EuiFlexItem>
Expand All @@ -132,7 +134,7 @@ interface RiskScoreMappingItemProps {
riskScoreMappingItem: RiskScoreMappingItemType;
}

const RiskScoreMappingItem = ({ riskScoreMappingItem }: RiskScoreMappingItemProps) => (
export const RiskScoreMappingItem = ({ riskScoreMappingItem }: RiskScoreMappingItemProps) => (
<EuiFlexGroup alignItems="center" gutterSize="s">
<OverrideColumn>
<EuiToolTip
Expand Down Expand Up @@ -218,15 +220,15 @@ interface ThreatProps {
threat: Threats;
}

const Threat = ({ threat }: ThreatProps) => (
export const Threat = ({ threat }: ThreatProps) => (
<ThreatEuiFlexGroup threat={filterEmptyThreats(threat)} data-test-subj="threatPropertyValue" />
);

interface ThreatIndicatorPathProps {
threatIndicatorPath: string;
}

const ThreatIndicatorPath = ({ threatIndicatorPath }: ThreatIndicatorPathProps) => (
export const ThreatIndicatorPath = ({ threatIndicatorPath }: ThreatIndicatorPathProps) => (
<EuiText size="s">{threatIndicatorPath}</EuiText>
);

Expand Down
Loading

0 comments on commit e308d17

Please sign in to comment.