Skip to content

Commit

Permalink
[8.x] [Security Solution] `FinalEdit`: Add `name`…
Browse files Browse the repository at this point in the history
… and `kql_query` fields + shared components (#193828) (#196191)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[Security Solution] `FinalEdit`: Add `name` and
`kql_query` fields + shared components
(#193828)](#193828)

<!--- Backport version: 9.4.3 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Nikita
Indik","email":"[email protected]"},"sourceCommit":{"committedDate":"2024-10-14T17:09:59Z","message":"[Security
Solution] `FinalEdit`: Add `name` and `kql_query` fields + shared
components (#193828)\n\n**Partially addresses:
https://github.com/elastic/kibana/issues/171520**\r\n**Is a follow-up PR
to: https://github.com/elastic/kibana/pull/192342**\r\n\r\n##
Summary\r\n\r\nChanges:\r\n - Adds editable components for `name` and
`kql_query` fields\r\n- Adds a `FieldFormWrapper` component that
abstracts away form creation\r\nand data preparation for each field\r\n-
Adds local context providers to pass data between the main context
and\r\nfield components\r\n- Adds some basic layout components to make
the \"edit\" functionality\r\nwork\r\n\r\n<img width=\"1392\"
alt=\"Scherm­afbeelding 2024-10-04 om 17 17
44\"\r\nsrc=\"https://github.com/user-attachments/assets/6272ac84-8159-4b8a-a0d4-88b458f4bc5f\">\r\n\r\n---------\r\n\r\nCo-authored-by:
Maxim Palenov
<[email protected]>","sha":"424ffbaffc6bdcec2634572d18cad5392ef0ace8","branchLabelMapping":{"^v9.0.0$":"main","^v8.16.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","v9.0.0","Team:Detections
and Resp","Team: SecuritySolution","Team:Detection Rule
Management","Feature:Prebuilt Detection
Rules","backport:prev-minor"],"title":"[Security Solution] `FinalEdit`:
Add `name` and `kql_query` fields + shared
components","number":193828,"url":"https://github.com/elastic/kibana/pull/193828","mergeCommit":{"message":"[Security
Solution] `FinalEdit`: Add `name` and `kql_query` fields + shared
components (#193828)\n\n**Partially addresses:
https://github.com/elastic/kibana/issues/171520**\r\n**Is a follow-up PR
to: https://github.com/elastic/kibana/pull/192342**\r\n\r\n##
Summary\r\n\r\nChanges:\r\n - Adds editable components for `name` and
`kql_query` fields\r\n- Adds a `FieldFormWrapper` component that
abstracts away form creation\r\nand data preparation for each field\r\n-
Adds local context providers to pass data between the main context
and\r\nfield components\r\n- Adds some basic layout components to make
the \"edit\" functionality\r\nwork\r\n\r\n<img width=\"1392\"
alt=\"Scherm­afbeelding 2024-10-04 om 17 17
44\"\r\nsrc=\"https://github.com/user-attachments/assets/6272ac84-8159-4b8a-a0d4-88b458f4bc5f\">\r\n\r\n---------\r\n\r\nCo-authored-by:
Maxim Palenov
<[email protected]>","sha":"424ffbaffc6bdcec2634572d18cad5392ef0ace8"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/193828","number":193828,"mergeCommit":{"message":"[Security
Solution] `FinalEdit`: Add `name` and `kql_query` fields + shared
components (#193828)\n\n**Partially addresses:
https://github.com/elastic/kibana/issues/171520**\r\n**Is a follow-up PR
to: https://github.com/elastic/kibana/pull/192342**\r\n\r\n##
Summary\r\n\r\nChanges:\r\n - Adds editable components for `name` and
`kql_query` fields\r\n- Adds a `FieldFormWrapper` component that
abstracts away form creation\r\nand data preparation for each field\r\n-
Adds local context providers to pass data between the main context
and\r\nfield components\r\n- Adds some basic layout components to make
the \"edit\" functionality\r\nwork\r\n\r\n<img width=\"1392\"
alt=\"Scherm­afbeelding 2024-10-04 om 17 17
44\"\r\nsrc=\"https://github.com/user-attachments/assets/6272ac84-8159-4b8a-a0d4-88b458f4bc5f\">\r\n\r\n---------\r\n\r\nCo-authored-by:
Maxim Palenov
<[email protected]>","sha":"424ffbaffc6bdcec2634572d18cad5392ef0ace8"}}]}]
BACKPORT-->

Co-authored-by: Nikita Indik <[email protected]>
  • Loading branch information
kibanamachine and nikitaindik authored Oct 14, 2024
1 parent d458824 commit 3db2504
Show file tree
Hide file tree
Showing 74 changed files with 1,242 additions and 118 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import * as i18n from './translations';
interface ComparisonSideProps<FieldName extends keyof DiffableAllFields> {
fieldName: FieldName;
fieldThreeWayDiff: ThreeWayDiff<DiffableAllFields[FieldName]>;
resolvedValue?: DiffableAllFields[FieldName];
resolvedValue: DiffableAllFields[FieldName];
}

export function ComparisonSide<FieldName extends keyof DiffableAllFields>({
Expand All @@ -39,6 +39,7 @@ export function ComparisonSide<FieldName extends keyof DiffableAllFields>({
const [oldVersionType, newVersionType] = selectedVersions.split('_') as [Version, Version];

const oldFieldValue = pickFieldValueForVersion(oldVersionType, fieldThreeWayDiff, resolvedValue);

const newFieldValue = pickFieldValueForVersion(newVersionType, fieldThreeWayDiff, resolvedValue);

const subfieldChanges = getSubfieldChanges(fieldName, oldFieldValue, newFieldValue);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ import type {
*
* @param version - The version for which the field value is to be picked.
* @param fieldThreeWayDiff - The three-way diff object containing the field values for different versions.
* @param resolvedValue - The user-set resolved value resolved value. Used if it is set and the version is final.
* @param resolvedValue - A value field will be upgraded to.
* @returns - The field value for the specified version
*/
export function pickFieldValueForVersion<FieldName extends keyof DiffableAllFields>(
version: Version,
fieldThreeWayDiff: ThreeWayDiff<DiffableAllFields[FieldName]>,
resolvedValue?: DiffableAllFields[FieldName]
resolvedValue: DiffableAllFields[FieldName]
): DiffableAllFields[FieldName] | undefined {
if (version === Version.Final) {
return resolvedValue;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* 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 { DiffableAllFields } from '../../../../../../../common/api/detection_engine';

type NonEditableFields = Readonly<Set<keyof DiffableAllFields>>;

/* These fields are not visible in the comparison UI and are not editable */
export const HIDDEN_FIELDS: NonEditableFields = new Set([
'alert_suppression',
'author',
'rule_id',
'license',
'version',
]);
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import { css } from '@emotion/css';
import { SplitAccordion } from '../../../../../../common/components/split_accordion';
import type {
DiffableAllFields,
DiffableRule,
RuleFieldsDiff,
ThreeWayDiff,
} from '../../../../../../../common/api/detection_engine';
Expand All @@ -20,23 +19,25 @@ import type { FieldUpgradeState } from '../../../../model/prebuilt_rule_upgrade'
import { ComparisonSide } from '../comparison_side/comparison_side';
import { FinalSide } from '../final_side/final_side';
import { FieldUpgradeConflictsResolverHeader } from './field_upgrade_conflicts_resolver_header';
import { useDiffableRuleContext } from '../diffable_rule_context';
import type { UpgradeableDiffableFields } from '../../../../model/prebuilt_rule_upgrade/fields';

interface FieldUpgradeConflictsResolverProps<FieldName extends keyof RuleFieldsDiff> {
interface FieldUpgradeConflictsResolverProps<FieldName extends UpgradeableDiffableFields> {
fieldName: FieldName;
fieldUpgradeState: FieldUpgradeState;
fieldThreeWayDiff: RuleFieldsDiff[FieldName];
finalDiffableRule: DiffableRule;
}

export function FieldUpgradeConflictsResolver<FieldName extends keyof RuleFieldsDiff>({
export function FieldUpgradeConflictsResolver<FieldName extends UpgradeableDiffableFields>({
fieldName,
fieldUpgradeState,
fieldThreeWayDiff,
finalDiffableRule,
}: FieldUpgradeConflictsResolverProps<FieldName>): JSX.Element {
const { euiTheme } = useEuiTheme();
const hasConflict = fieldThreeWayDiff.conflict !== ThreeWayDiffConflict.NONE;

const { finalDiffableRule } = useDiffableRuleContext();

return (
<>
<SplitAccordion
Expand Down Expand Up @@ -65,7 +66,7 @@ export function FieldUpgradeConflictsResolver<FieldName extends keyof RuleFields
`}
/>
<EuiFlexItem grow={1}>
<FinalSide fieldName={fieldName} finalDiffableRule={finalDiffableRule} />
<FinalSide fieldName={fieldName} />
</EuiFlexItem>
</EuiFlexGroup>
</SplitAccordion>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,12 @@ export function RuleUpgradeCallout({ ruleUpgradeState }: RuleUpgradeCalloutProps
}

return (
<EuiCallOut title={i18n.RULE_IS_READY_FOR_UPGRADE} iconType="warning" color="success" size="s">
<EuiCallOut
title={i18n.RULE_IS_READY_FOR_UPGRADE}
iconType="checkInCircleFilled"
color="success"
size="s"
>
<p>{i18n.RULE_IS_READY_FOR_UPGRADE_DESCRIPTION}</p>
</EuiCallOut>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const RULE_HAS_NON_SOLVABLE_CONFLICTS = (count: number) =>
{
values: { count },
defaultMessage:
'{count} of the fields has a unsolved conflict. Please review and modify accordingly.',
'{count} of the fields {count, plural, one {has} other {have}} an unsolved conflict. Please review and modify accordingly.',
}
);

Expand All @@ -31,7 +31,7 @@ export const RULE_HAS_SOLVABLE_CONFLICTS = (count: number) =>
{
values: { count },
defaultMessage:
'{count} of the fields has an update conflict, please review the suggested update being updating.',
'{count} of the fields {count, plural, one {has} other {have}} an update conflict, please review the suggested update being updating.',
}
);

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

import React from 'react';
import type {
RuleUpgradeState,
SetRuleFieldResolvedValueFn,
} from '../../../../model/prebuilt_rule_upgrade';
import type { RuleUpgradeState } from '../../../../model/prebuilt_rule_upgrade';
import { FieldUpgradeConflictsResolver } from './field_upgrade_conflicts_resolver';
import type { NonUpgradeableDiffableFields } from '../../../../model/prebuilt_rule_upgrade/fields';
import { isNonUpgradeableFieldName } from '../../../../model/prebuilt_rule_upgrade/fields';

type FieldDiffEntries<FieldsDiff, ExcludedFields extends keyof FieldsDiff = never> = Array<
[
Exclude<keyof FieldsDiff, ExcludedFields>,
Required<FieldsDiff>[Exclude<keyof FieldsDiff, ExcludedFields>]
]
>;

interface RuleUpgradeConflictsResolverProps {
ruleUpgradeState: RuleUpgradeState;
setRuleFieldResolvedValue: SetRuleFieldResolvedValueFn;
}

export function RuleUpgradeConflictsResolver({
ruleUpgradeState,
setRuleFieldResolvedValue,
}: RuleUpgradeConflictsResolverProps): JSX.Element {
const fieldDiffEntries = Object.entries(ruleUpgradeState.diff.fields) as Array<
[
keyof typeof ruleUpgradeState.diff.fields,
Required<typeof ruleUpgradeState.diff.fields>[keyof typeof ruleUpgradeState.diff.fields]
]
}: RuleUpgradeConflictsResolverProps): React.ReactNode {
const fieldDiffEntries = Object.entries(ruleUpgradeState.diff.fields) as FieldDiffEntries<
typeof ruleUpgradeState.diff.fields
>;
const fields = fieldDiffEntries.map(([fieldName, fieldDiff]) => (

const fields = fieldDiffEntries.filter(([fieldName]) => {
return isNonUpgradeableFieldName(fieldName) === false;
}) as FieldDiffEntries<typeof ruleUpgradeState.diff.fields, NonUpgradeableDiffableFields>;

return fields.map(([fieldName, fieldDiff]) => (
<FieldUpgradeConflictsResolver
key={fieldName}
fieldName={fieldName}
fieldUpgradeState={ruleUpgradeState.fieldsUpgradeState[fieldName]}
fieldThreeWayDiff={fieldDiff}
finalDiffableRule={ruleUpgradeState.finalRule}
/>
));

return <>{fields}</>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* 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, { createContext, useContext } from 'react';
import type { DiffableRule } from '../../../../../../common/api/detection_engine';
import { invariant } from '../../../../../../common/utils/invariant';
import type { SetRuleFieldResolvedValueFn } from '../../../model/prebuilt_rule_upgrade/set_rule_field_resolved_value';

interface DiffableRuleContextType {
finalDiffableRule: DiffableRule;
setRuleFieldResolvedValue: SetRuleFieldResolvedValueFn;
}

const DiffableRuleContext = createContext<DiffableRuleContextType | null>(null);

interface DiffableRuleContextProviderProps {
finalDiffableRule: DiffableRule;
setRuleFieldResolvedValue: SetRuleFieldResolvedValueFn;
children: React.ReactNode;
}

export function DiffableRuleContextProvider({
finalDiffableRule,
setRuleFieldResolvedValue,
children,
}: DiffableRuleContextProviderProps) {
const contextValue = {
finalDiffableRule,
setRuleFieldResolvedValue,
};

return (
<DiffableRuleContext.Provider value={contextValue}>{children}</DiffableRuleContext.Provider>
);
}

export function useDiffableRuleContext() {
const context = useContext(DiffableRuleContext);

invariant(
context !== null,
'useDiffableRuleContext must be used inside a DiffableRuleContextProvider'
);

return context;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* 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 { FieldFormWrapper } from './field_form_wrapper';
import { NameEdit, nameSchema } from './fields/name';
import type { UpgradeableCommonFields } from '../../../../model/prebuilt_rule_upgrade/fields';
interface CommonRuleFieldEditProps {
fieldName: UpgradeableCommonFields;
}

export function CommonRuleFieldEdit({ fieldName }: CommonRuleFieldEditProps) {
switch (fieldName) {
case 'name':
return <FieldFormWrapper component={NameEdit} fieldFormSchema={nameSchema} />;
default:
return null; // Will be replaced with `assertUnreachable(fieldName)` once all fields are implemented
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* 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 { FieldFormWrapper } from './field_form_wrapper';
import {
KqlQueryEdit,
kqlQuerySchema,
kqlQuerySerializer,
kqlQueryDeserializer,
} from './fields/kql_query';
import type { UpgradeableCustomQueryFields } from '../../../../model/prebuilt_rule_upgrade/fields';

interface CustomQueryRuleFieldEditProps {
fieldName: UpgradeableCustomQueryFields;
}

export function CustomQueryRuleFieldEdit({ fieldName }: CustomQueryRuleFieldEditProps) {
switch (fieldName) {
case 'kql_query':
return (
<FieldFormWrapper
component={KqlQueryEdit}
fieldFormSchema={kqlQuerySchema}
serializer={kqlQuerySerializer}
deserializer={kqlQueryDeserializer}
/>
);
default:
return null; // Will be replaced with `assertUnreachable(fieldName)` once all fields are implemented
}
}
Loading

0 comments on commit 3db2504

Please sign in to comment.