diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/review_rule_upgrade/review_rule_upgrade_route.ts b/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/review_rule_upgrade/review_rule_upgrade_route.ts index 67f000a41711e..2f2d6e3bd1c26 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/review_rule_upgrade/review_rule_upgrade_route.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/review_rule_upgrade/review_rule_upgrade_route.ts @@ -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 */ diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/helpers.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/helpers.ts index 73dbe92cebbe4..9810cd4882cc7 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/helpers.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/helpers.ts @@ -5,8 +5,13 @@ * 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, @@ -14,6 +19,8 @@ import { 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 = []; @@ -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); +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_about_section.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_about_section.tsx index 7f52bae481160..c7c9ec3ca732d 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_about_section.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_about_section.tsx @@ -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; `; @@ -86,7 +86,7 @@ interface SeverityMappingItemProps { severityMappingItem: SeverityMappingItemType; } -const SeverityMappingItem = ({ severityMappingItem }: SeverityMappingItemProps) => ( +export const SeverityMappingItem = ({ severityMappingItem }: SeverityMappingItemProps) => ( {`${severityMappingItem.field}:`} - + - - {defaultToEmptyTag(severityMappingItem.value)} - + + + {defaultToEmptyTag(severityMappingItem.value)} + + - + @@ -132,7 +134,7 @@ interface RiskScoreMappingItemProps { riskScoreMappingItem: RiskScoreMappingItemType; } -const RiskScoreMappingItem = ({ riskScoreMappingItem }: RiskScoreMappingItemProps) => ( +export const RiskScoreMappingItem = ({ riskScoreMappingItem }: RiskScoreMappingItemProps) => ( ( +export const Threat = ({ threat }: ThreatProps) => ( ); @@ -226,7 +228,7 @@ interface ThreatIndicatorPathProps { threatIndicatorPath: string; } -const ThreatIndicatorPath = ({ threatIndicatorPath }: ThreatIndicatorPathProps) => ( +export const ThreatIndicatorPath = ({ threatIndicatorPath }: ThreatIndicatorPathProps) => ( {threatIndicatorPath} ); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_definition_section.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_definition_section.tsx index 35f429f776fab..e0b0431138508 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_definition_section.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_definition_section.tsx @@ -57,12 +57,14 @@ import { queryStyles, useRequiredFieldsStyles, } from './rule_definition_section.styles'; +import { getQueryLanguageLabel } from './helpers'; +import { useDefaultIndexPattern } from './use_default_index_pattern'; interface SavedQueryNameProps { savedQueryName: string; } -const SavedQueryName = ({ savedQueryName }: SavedQueryNameProps) => ( +export const SavedQueryName = ({ savedQueryName }: SavedQueryNameProps) => ( {savedQueryName} @@ -75,12 +77,19 @@ interface FiltersProps { 'data-test-subj'?: string; } -const Filters = ({ filters, dataViewId, index, 'data-test-subj': dataTestSubj }: FiltersProps) => { +export const Filters = ({ + filters, + dataViewId, + index, + 'data-test-subj': dataTestSubj, +}: FiltersProps) => { const flattenedFilters = mapAndFlattenFilters(filters); + const defaultIndexPattern = useDefaultIndexPattern(); + const { indexPattern } = useRuleIndexPattern({ dataSourceType: dataViewId ? DataSourceType.DataView : DataSourceType.IndexPatterns, - index: index ?? [], + index: index ?? defaultIndexPattern, dataViewId, }); @@ -104,7 +113,7 @@ interface QueryProps { 'data-test-subj'?: string; } -const Query = ({ query, 'data-test-subj': dataTestSubj = 'query' }: QueryProps) => { +export const Query = ({ query, 'data-test-subj': dataTestSubj = 'query' }: QueryProps) => { const styles = queryStyles; return (
@@ -117,7 +126,7 @@ interface IndexProps { index: string[]; } -const Index = ({ index }: IndexProps) => ( +export const Index = ({ index }: IndexProps) => ( ); @@ -125,7 +134,7 @@ interface DataViewIdProps { dataViewId: string; } -const DataViewId = ({ dataViewId }: DataViewIdProps) => ( +export const DataViewId = ({ dataViewId }: DataViewIdProps) => ( {dataViewId} @@ -135,7 +144,7 @@ interface DataViewIndexPatternProps { dataViewId: string; } -const DataViewIndexPattern = ({ dataViewId }: DataViewIndexPatternProps) => { +export const DataViewIndexPattern = ({ dataViewId }: DataViewIndexPatternProps) => { const { data } = useKibana().services; const [indexPattern, setIndexPattern] = React.useState(''); const [hasError, setHasError] = React.useState(false); @@ -191,18 +200,24 @@ const AnomalyThreshold = ({ anomalyThreshold }: AnomalyThresholdProps) => ( ); interface MachineLearningJobListProps { - jobIds: string[]; + jobIds?: string | string[]; isInteractive: boolean; } -const MachineLearningJobList = ({ jobIds, isInteractive }: MachineLearningJobListProps) => { +export const MachineLearningJobList = ({ jobIds, isInteractive }: MachineLearningJobListProps) => { const { jobs } = useSecurityJobs(); + if (!jobIds) { + return null; + } + + const jobIdsArray = Array.isArray(jobIds) ? jobIds : [jobIds]; + if (isInteractive) { - return ; + return ; } - const relevantJobs = jobs.filter((job) => jobIds.includes(job.id)); + const relevantJobs = jobs.filter((job) => jobIdsArray.includes(job.id)); return ( <> @@ -251,7 +266,7 @@ interface RequiredFieldsProps { requiredFields: RequiredFieldArray; } -const RequiredFields = ({ requiredFields }: RequiredFieldsProps) => { +export const RequiredFields = ({ requiredFields }: RequiredFieldsProps) => { const styles = useRequiredFieldsStyles(); return ( @@ -293,7 +308,7 @@ interface ThreatIndexProps { threatIndex: string[]; } -const ThreatIndex = ({ threatIndex }: ThreatIndexProps) => ( +export const ThreatIndex = ({ threatIndex }: ThreatIndexProps) => ( ); @@ -301,7 +316,7 @@ interface ThreatMappingProps { threatMapping: ThreatMappingType; } -const ThreatMapping = ({ threatMapping }: ThreatMappingProps) => { +export const ThreatMapping = ({ threatMapping }: ThreatMappingProps) => { const description = threatMapping.reduce( (accumThreatMaps, threatMap, threatMapIndex, { length: threatMappingLength }) => { const matches = threatMap.entries.reduce( @@ -434,14 +449,28 @@ const prepareDefinitionSectionListItems = ( } if (savedQuery) { - definitionSectionListItems.push({ - title: ( - - {descriptionStepI18n.SAVED_QUERY_NAME_LABEL} - - ), - description: , - }); + definitionSectionListItems.push( + { + title: ( + + {descriptionStepI18n.SAVED_QUERY_NAME_LABEL} + + ), + description: , + }, + { + title: ( + + {i18n.SAVED_QUERY_LANGUAGE_LABEL} + + ), + description: ( + + {getQueryLanguageLabel(savedQuery.attributes.query.language)} + + ), + } + ); if (savedQuery.attributes.filters) { definitionSectionListItems.push({ @@ -452,8 +481,10 @@ const prepareDefinitionSectionListItems = ( ), description: ( ), }); @@ -508,12 +539,26 @@ const prepareDefinitionSectionListItems = ( description: , }); } else { - definitionSectionListItems.push({ - title: ( - {descriptionStepI18n.QUERY_LABEL} - ), - description: , - }); + definitionSectionListItems.push( + { + title: ( + {descriptionStepI18n.QUERY_LABEL} + ), + description: , + }, + { + title: ( + + {i18n.QUERY_LANGUAGE_LABEL} + + ), + description: ( + + {getQueryLanguageLabel(rule.language || '')} + + ), + } + ); } } @@ -542,7 +587,7 @@ const prepareDefinitionSectionListItems = ( ), description: ( ), @@ -633,6 +678,21 @@ const prepareDefinitionSectionListItems = ( }); } + if ('threat_language' in rule && rule.threat_language) { + definitionSectionListItems.push({ + title: ( + + {i18n.THREAT_QUERY_LANGUAGE_LABEL} + + ), + description: ( + + {getQueryLanguageLabel(rule.threat_language)} + + ), + }); + } + if ('new_terms_fields' in rule && rule.new_terms_fields && rule.new_terms_fields.length > 0) { definitionSectionListItems.push({ title: ( diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/field_readonly.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/field_readonly.tsx new file mode 100644 index 0000000000000..f3d436ad7a26a --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/field_readonly.tsx @@ -0,0 +1,89 @@ +/* + * 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 type { DiffableAllFields } from '../../../../../../../common/api/detection_engine'; +import { KqlQueryReadOnly } from './fields/kql_query'; +import { DataSourceReadOnly } from './fields/data_source/data_source'; +import { EqlQueryReadOnly } from './fields/eql_query/eql_query'; +import { EsqlQueryReadOnly } from './fields/esql_query/esql_query'; +import { MachineLearningJobIdReadOnly } from './fields/machine_learning_job_id/machine_learning_job_id'; +import { RelatedIntegrationsReadOnly } from './fields/related_integrations/related_integrations'; +import { RequiredFieldsReadOnly } from './fields/required_fields/required_fields'; +import { SeverityMappingReadOnly } from './fields/severity_mapping/severity_mapping'; +import { RiskScoreMappingReadOnly } from './fields/risk_score_mapping/risk_score_mapping'; +import { ThreatMappingReadOnly } from './fields/threat_mapping/threat_mapping'; +import { ThreatReadOnly } from './fields/threat/threat'; +import { ThreatIndexReadOnly } from './fields/threat_index/threat_index'; +import { ThreatIndicatorPathReadOnly } from './fields/threat_indicator_path/threat_indicator_path'; +import { ThreatQueryReadOnly } from './fields/threat_query/threat_query'; + +interface FieldReadOnlyProps { + fieldName: keyof DiffableAllFields; + finalDiffableRule: DiffableAllFields; +} + +export function FieldReadOnly({ fieldName, finalDiffableRule }: FieldReadOnlyProps) { + switch (fieldName) { + case 'data_source': + return ; + case 'eql_query': + return ( + + ); + case 'esql_query': + return ; + case 'kql_query': + return ( + + ); + case 'machine_learning_job_id': + return ( + + ); + case 'related_integrations': + return ( + + ); + case 'required_fields': + return ; + case 'risk_score_mapping': + return ; + case 'severity_mapping': + return ; + case 'threat': + return ; + case 'threat_index': + return ; + case 'threat_indicator_path': + return ( + + ); + case 'threat_mapping': + return ; + case 'threat_query': + return ( + + ); + default: + return null; + } +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/data_source/data_source.stories.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/data_source/data_source.stories.tsx new file mode 100644 index 0000000000000..6ffe47a254de9 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/data_source/data_source.stories.tsx @@ -0,0 +1,61 @@ +/* + * 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 type { Story } from '@storybook/react'; +import { FieldReadOnly } from '../../field_readonly'; +import type { DiffableAllFields } from '../../../../../../../../../common/api/detection_engine'; +import { ThreeWayDiffStorybookProviders } from '../../storybook/three_way_diff_storybook_providers'; +import { + dataSourceWithDataView, + dataSourceWithIndexPatterns, + mockDataView, +} from '../../storybook/mocks'; + +export default { + component: FieldReadOnly, + title: 'Rule Management/Prebuilt Rules/Upgrade Flyout/ThreeWayDiff/FieldReadOnly/data_source', +}; + +interface TemplateProps { + finalDiffableRule: Partial; + kibanaServicesMock?: Record; +} + +const Template: Story = (args) => { + return ( + + + + ); +}; + +export const DataSourceWithIndexPatterns = Template.bind({}); + +DataSourceWithIndexPatterns.args = { + finalDiffableRule: { + data_source: dataSourceWithIndexPatterns, + }, +}; + +export const DataSourceWithDataView = Template.bind({}); + +DataSourceWithDataView.args = { + finalDiffableRule: { + data_source: dataSourceWithDataView, + }, + kibanaServicesMock: { + data: { + dataViews: { + get: async () => mockDataView(), + }, + }, + }, +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/data_source/data_source.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/data_source/data_source.tsx new file mode 100644 index 0000000000000..29518981011fa --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/data_source/data_source.tsx @@ -0,0 +1,56 @@ +/* + * 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 { EuiDescriptionList } from '@elastic/eui'; +import { DataSourceType } from '../../../../../../../../../common/api/detection_engine'; +import type { DiffableAllFields } from '../../../../../../../../../common/api/detection_engine'; +import { Index, DataViewId, DataViewIndexPattern } from '../../../../rule_definition_section'; +import * as ruleDetailsI18n from '../../../../translations'; +import { assertUnreachable } from '../../../../../../../../../common/utility_types'; + +interface DataSourceReadOnlyProps { + dataSource: DiffableAllFields['data_source']; +} + +export function DataSourceReadOnly({ dataSource }: DataSourceReadOnlyProps) { + if (!dataSource) { + return null; + } + + if (dataSource.type === DataSourceType.index_patterns) { + return ( + , + }, + ]} + /> + ); + } + + if (dataSource.type === DataSourceType.data_view) { + return ( + , + }, + { + title: ruleDetailsI18n.DATA_VIEW_INDEX_PATTERN_FIELD_LABEL, + description: , + }, + ]} + /> + ); + } + + return assertUnreachable(dataSource); +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/eql_query/eql_query.stories.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/eql_query/eql_query.stories.tsx new file mode 100644 index 0000000000000..205e87e209068 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/eql_query/eql_query.stories.tsx @@ -0,0 +1,72 @@ +/* + * 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 type { Story } from '@storybook/react'; +import { FieldReadOnly } from '../../field_readonly'; +import type { DiffableAllFields } from '../../../../../../../../../common/api/detection_engine'; +import { ThreeWayDiffStorybookProviders } from '../../storybook/three_way_diff_storybook_providers'; +import { EqlQueryReadOnly } from './eql_query'; +import { + dataSourceWithDataView, + dataSourceWithIndexPatterns, + eqlQuery, + mockDataView, +} from '../../storybook/mocks'; + +export default { + component: EqlQueryReadOnly, + title: 'Rule Management/Prebuilt Rules/Upgrade Flyout/ThreeWayDiff/FieldReadOnly/eql_query', +}; + +interface TemplateProps { + finalDiffableRule: Partial; + kibanaServicesMock?: Record; +} + +const Template: Story = (args) => { + return ( + + + + ); +}; + +export const EqlQueryWithIndexPatterns = Template.bind({}); + +EqlQueryWithIndexPatterns.args = { + finalDiffableRule: { + eql_query: eqlQuery, + data_source: dataSourceWithIndexPatterns, + }, + kibanaServicesMock: { + data: { + dataViews: { + create: async () => mockDataView(), + }, + }, + }, +}; + +export const EqlQueryWithDataView = Template.bind({}); + +EqlQueryWithDataView.args = { + finalDiffableRule: { + eql_query: eqlQuery, + data_source: dataSourceWithDataView, + }, + kibanaServicesMock: { + data: { + dataViews: { + get: async () => mockDataView(), + }, + }, + }, +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/eql_query/eql_query.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/eql_query/eql_query.tsx new file mode 100644 index 0000000000000..3238e7bb7eeef --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/eql_query/eql_query.tsx @@ -0,0 +1,41 @@ +/* + * 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 { EuiDescriptionList } from '@elastic/eui'; +import type { EuiDescriptionListProps } from '@elastic/eui'; +import type { DiffableAllFields } from '../../../../../../../../../common/api/detection_engine'; +import * as descriptionStepI18n from '../../../../../../../rule_creation_ui/components/description_step/translations'; +import { Query, Filters } from '../../../../rule_definition_section'; +import { getDataSourceProps, typeCheckFilters } from '../../../../helpers'; + +interface EqlQueryReadOnlyProps { + eqlQuery: DiffableAllFields['eql_query']; + dataSource: DiffableAllFields['data_source']; +} + +export function EqlQueryReadOnly({ eqlQuery, dataSource }: EqlQueryReadOnlyProps) { + const listItems: EuiDescriptionListProps['listItems'] = [ + { + title: descriptionStepI18n.EQL_QUERY_LABEL, + description: , + }, + ]; + + const filters = typeCheckFilters(eqlQuery.filters); + + if (filters.length > 0) { + const dataSourceProps = getDataSourceProps(dataSource); + + listItems.push({ + title: descriptionStepI18n.FILTERS_LABEL, + description: , + }); + } + + return ; +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/esql_query/esql_query.stories.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/esql_query/esql_query.stories.tsx new file mode 100644 index 0000000000000..cda1c99a218bb --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/esql_query/esql_query.stories.tsx @@ -0,0 +1,40 @@ +/* + * 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 type { Story } from '@storybook/react'; +import { FieldReadOnly } from '../../field_readonly'; +import type { DiffableAllFields } from '../../../../../../../../../common/api/detection_engine'; + +export default { + component: FieldReadOnly, + title: 'Rule Management/Prebuilt Rules/Upgrade Flyout/ThreeWayDiff/FieldReadOnly/esql_query', +}; + +interface TemplateProps { + finalDiffableRule: Partial; +} + +const Template: Story = (args) => { + return ( + + ); +}; + +export const Default = Template.bind({}); + +Default.args = { + finalDiffableRule: { + esql_query: { + query: `SELECT user.name, source.ip FROM "logs-*" WHERE event.action = 'user_login' AND event.outcome = 'failure'`, + language: 'esql', + }, + }, +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/esql_query/esql_query.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/esql_query/esql_query.tsx new file mode 100644 index 0000000000000..ec110bd034b67 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/esql_query/esql_query.tsx @@ -0,0 +1,29 @@ +/* + * 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 { EuiDescriptionList } from '@elastic/eui'; +import * as descriptionStepI18n from '../../../../../../../rule_creation_ui/components/description_step/translations'; +import { Query } from '../../../../rule_definition_section'; +import type { DiffableAllFields } from '../../../../../../../../../common/api/detection_engine'; + +interface EsqlQueryReadonlyProps { + esqlQuery: DiffableAllFields['esql_query']; +} + +export function EsqlQueryReadOnly({ esqlQuery }: EsqlQueryReadonlyProps) { + return ( + , + }, + ]} + /> + ); +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/kql_query/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/kql_query/index.tsx new file mode 100644 index 0000000000000..a2dd75e188a0c --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/kql_query/index.tsx @@ -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 { KqlQueryType } from '../../../../../../../../../common/api/detection_engine'; +import type { + DiffableAllFields, + RuleKqlQuery, +} from '../../../../../../../../../common/api/detection_engine'; +import { InlineKqlQueryReadOnly } from './inline_kql_query'; +import { SavedKqlQueryReadOnly } from './saved_kql_query'; +import { assertUnreachable } from '../../../../../../../../../common/utility_types'; + +interface KqlQueryReadOnlyProps { + kqlQuery: RuleKqlQuery; + dataSource: DiffableAllFields['data_source']; + ruleType: DiffableAllFields['type']; +} + +export function KqlQueryReadOnly({ kqlQuery, dataSource, ruleType }: KqlQueryReadOnlyProps) { + if (kqlQuery.type === KqlQueryType.inline_query) { + return ; + } + + if (kqlQuery.type === KqlQueryType.saved_query) { + return ( + + ); + } + + return assertUnreachable(kqlQuery); +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/kql_query/inline_kql_query.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/kql_query/inline_kql_query.tsx new file mode 100644 index 0000000000000..a70909745d8dc --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/kql_query/inline_kql_query.tsx @@ -0,0 +1,64 @@ +/* + * 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 { EuiDescriptionList } from '@elastic/eui'; +import type { EuiDescriptionListProps } from '@elastic/eui'; +import type { + DiffableAllFields, + InlineKqlQuery, +} from '../../../../../../../../../common/api/detection_engine'; +import { Query, Filters } from '../../../../rule_definition_section'; +import * as ruleDetailsI18n from '../../../../translations'; +import * as descriptionStepI18n from '../../../../../../../rule_creation_ui/components/description_step/translations'; +import { getDataSourceProps, getQueryLanguageLabel, typeCheckFilters } from '../../../../helpers'; + +const defaultI18nLabels = { + query: descriptionStepI18n.QUERY_LABEL, + language: ruleDetailsI18n.QUERY_LANGUAGE_LABEL, + filters: descriptionStepI18n.FILTERS_LABEL, +}; + +interface InlineQueryProps { + kqlQuery: InlineKqlQuery; + dataSource?: DiffableAllFields['data_source']; + i18nLabels?: { + query: string; + language: string; + filters: string; + }; +} + +export function InlineKqlQueryReadOnly({ + kqlQuery, + dataSource, + i18nLabels = defaultI18nLabels, +}: InlineQueryProps) { + const listItems: EuiDescriptionListProps['listItems'] = [ + { + title: i18nLabels.query, + description: , + }, + { + title: i18nLabels.language, + description: getQueryLanguageLabel(kqlQuery.language), + }, + ]; + + const filters = typeCheckFilters(kqlQuery.filters); + + if (filters.length > 0) { + const dataSourceProps = getDataSourceProps(dataSource); + + listItems.push({ + title: i18nLabels.filters, + description: , + }); + } + + return ; +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/kql_query/kql_query.stories.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/kql_query/kql_query.stories.tsx new file mode 100644 index 0000000000000..61d31d983b183 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/kql_query/kql_query.stories.tsx @@ -0,0 +1,135 @@ +/* + * 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 type { Story } from '@storybook/react'; +import { FieldReadOnly } from '../../field_readonly'; +import type { + DiffableAllFields, + RuleKqlQuery, +} from '../../../../../../../../../common/api/detection_engine'; +import { ThreeWayDiffStorybookProviders } from '../../storybook/three_way_diff_storybook_providers'; +import { + dataSourceWithDataView, + dataSourceWithIndexPatterns, + inlineKqlQuery, + mockDataView, + savedKqlQuery, + savedQueryResponse, +} from '../../storybook/mocks'; + +export default { + component: FieldReadOnly, + title: 'Rule Management/Prebuilt Rules/Upgrade Flyout/ThreeWayDiff/FieldReadOnly/kql_query', +}; + +interface TemplateProps { + finalDiffableRule: Partial | { kql_query: RuleKqlQuery }; + kibanaServicesMock?: Record; +} + +const Template: Story = (args) => { + return ( + + + + ); +}; + +export const InlineKqlQueryWithIndexPatterns = Template.bind({}); + +InlineKqlQueryWithIndexPatterns.args = { + finalDiffableRule: { + kql_query: inlineKqlQuery, + data_source: dataSourceWithIndexPatterns, + }, + kibanaServicesMock: { + data: { + dataViews: { + create: async () => mockDataView(), + }, + }, + }, +}; + +export const InlineKqlQueryWithDataView = Template.bind({}); + +InlineKqlQueryWithDataView.args = { + finalDiffableRule: { + kql_query: inlineKqlQuery, + data_source: dataSourceWithDataView, + }, + kibanaServicesMock: { + data: { + dataViews: { + get: async () => mockDataView(), + }, + }, + }, +}; + +export const InlineKqlQueryWithoutDataSource = Template.bind({}); + +/* + Filters should still be displayed if no `data_source` is provided. + Component would fall back to the default index pattern in such case. +*/ +InlineKqlQueryWithoutDataSource.args = { + finalDiffableRule: { + kql_query: inlineKqlQuery, + }, + kibanaServicesMock: { + data: { + dataViews: { + create: async () => mockDataView(), + }, + }, + }, +}; + +export const SavedKqlQueryWithIndexPatterns = Template.bind({}); + +SavedKqlQueryWithIndexPatterns.args = { + finalDiffableRule: { + kql_query: savedKqlQuery, + data_source: dataSourceWithIndexPatterns, + type: 'saved_query', + }, + kibanaServicesMock: { + data: { + dataViews: { + create: async () => mockDataView(), + }, + }, + http: { + get: async () => savedQueryResponse, + }, + }, +}; + +export const SavedKqlQueryWithDataView = Template.bind({}); + +SavedKqlQueryWithDataView.args = { + finalDiffableRule: { + kql_query: savedKqlQuery, + data_source: dataSourceWithDataView, + type: 'saved_query', + }, + kibanaServicesMock: { + data: { + dataViews: { + get: async () => mockDataView(), + }, + }, + http: { + get: async () => savedQueryResponse, + }, + }, +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/kql_query/saved_kql_query.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/kql_query/saved_kql_query.tsx new file mode 100644 index 0000000000000..d7741f41af820 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/kql_query/saved_kql_query.tsx @@ -0,0 +1,66 @@ +/* + * 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 { EuiDescriptionList } from '@elastic/eui'; +import type { EuiDescriptionListProps } from '@elastic/eui'; +import type { + SavedKqlQuery, + DiffableRule, + DiffableAllFields, +} from '../../../../../../../../../common/api/detection_engine'; +import { Query, SavedQueryName, Filters } from '../../../../rule_definition_section'; +import * as ruleDetailsI18n from '../../../../translations'; +import * as descriptionStepI18n from '../../../../../../../rule_creation_ui/components/description_step/translations'; +import { useGetSavedQuery } from '../../../../../../../../detections/pages/detection_engine/rules/use_get_saved_query'; +import { getDataSourceProps, getQueryLanguageLabel } from '../../../../helpers'; + +interface SavedQueryProps { + kqlQuery: SavedKqlQuery; + dataSource?: DiffableAllFields['data_source']; + ruleType: DiffableRule['type']; +} + +export function SavedKqlQueryReadOnly({ kqlQuery, dataSource, ruleType }: SavedQueryProps) { + const { savedQuery } = useGetSavedQuery({ + savedQueryId: kqlQuery.saved_query_id, + ruleType, + }); + + if (!savedQuery) { + return null; + } + + const listItems: EuiDescriptionListProps['listItems'] = [ + { + title: descriptionStepI18n.SAVED_QUERY_NAME_LABEL, + description: , + }, + { + title: ruleDetailsI18n.SAVED_QUERY_LANGUAGE_LABEL, + description: getQueryLanguageLabel(savedQuery.attributes.query.language), + }, + ]; + + if (typeof savedQuery.attributes.query.query === 'string') { + listItems.push({ + title: descriptionStepI18n.SAVED_QUERY_LABEL, + description: , + }); + } + + if (savedQuery.attributes.filters) { + const dataSourceProps = getDataSourceProps(dataSource); + + listItems.push({ + title: descriptionStepI18n.SAVED_QUERY_FILTERS_LABEL, + description: , + }); + } + + return ; +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/machine_learning_job_id/machine_learning_job_id.stories.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/machine_learning_job_id/machine_learning_job_id.stories.tsx new file mode 100644 index 0000000000000..4d334f3aa57ec --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/machine_learning_job_id/machine_learning_job_id.stories.tsx @@ -0,0 +1,83 @@ +/* + * 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 { useQueryClient } from '@tanstack/react-query'; +import type { Story } from '@storybook/react'; +import { MachineLearningJobIdReadOnly } from './machine_learning_job_id'; +import type { DiffableAllFields } from '../../../../../../../../../common/api/detection_engine'; +import { FieldReadOnly } from '../../field_readonly'; +import { ThreeWayDiffStorybookProviders } from '../../storybook/three_way_diff_storybook_providers'; +import { GET_MODULES_QUERY_KEY } from '../../../../../../../../common/components/ml_popover/hooks/use_fetch_modules_query'; +import { GET_RECOGNIZER_QUERY_KEY } from '../../../../../../../../common/components/ml_popover/hooks/use_fetch_recognizer_query'; +import { GET_JOBS_SUMMARY_QUERY_KEY } from '../../../../../../../../common/components/ml/hooks/use_fetch_jobs_summary_query'; + +export default { + component: MachineLearningJobIdReadOnly, + title: + 'Rule Management/Prebuilt Rules/Upgrade Flyout/ThreeWayDiff/FieldReadOnly/machine_learning_job_id', +}; + +const mockedModulesData = [ + { + id: 'security_auth', + jobs: [ + { + id: 'auth_high_count_logon_events', + config: { + groups: [], + custom_settings: { + security_app_display_name: 'Spike in Logon Events', + }, + }, + }, + ], + }, +]; + +const mockedCompatibleModules = [ + { + id: 'security_auth', + }, +]; + +function MockMlData({ children }: { children: React.ReactNode }) { + const queryClient = useQueryClient(); + + queryClient.setQueryData([GET_JOBS_SUMMARY_QUERY_KEY, {}], []); + + queryClient.setQueryData([GET_MODULES_QUERY_KEY, {}], mockedModulesData); + + queryClient.setQueryData([GET_RECOGNIZER_QUERY_KEY, {}], mockedCompatibleModules); + + return <>{children}; +} + +interface TemplateProps { + finalDiffableRule: Partial; +} + +const Template: Story = (args) => { + return ( + + + + + + ); +}; + +export const Default = Template.bind({}); + +Default.args = { + finalDiffableRule: { + machine_learning_job_id: 'auth_high_count_logon_events', + }, +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/machine_learning_job_id/machine_learning_job_id.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/machine_learning_job_id/machine_learning_job_id.tsx new file mode 100644 index 0000000000000..b177da1467910 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/machine_learning_job_id/machine_learning_job_id.tsx @@ -0,0 +1,31 @@ +/* + * 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 { EuiDescriptionList } from '@elastic/eui'; +import { MachineLearningJobList } from '../../../../rule_definition_section'; +import type { DiffableAllFields } from '../../../../../../../../../common/api/detection_engine'; +import * as ruleDetailsI18n from '../../../../translations'; + +interface MachineLearningJobIdProps { + machineLearningJobId: DiffableAllFields['machine_learning_job_id']; +} + +export function MachineLearningJobIdReadOnly({ machineLearningJobId }: MachineLearningJobIdProps) { + return ( + + ), + }, + ]} + /> + ); +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/related_integrations/related_integrations.stories.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/related_integrations/related_integrations.stories.tsx new file mode 100644 index 0000000000000..9855e2f974096 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/related_integrations/related_integrations.stories.tsx @@ -0,0 +1,64 @@ +/* + * 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 { useQueryClient } from '@tanstack/react-query'; +import type { Story } from '@storybook/react'; +import { RelatedIntegrationsReadOnly } from './related_integrations'; +import { ThreeWayDiffStorybookProviders } from '../../storybook/three_way_diff_storybook_providers'; +import { FieldReadOnly } from '../../field_readonly'; +import type { DiffableAllFields } from '../../../../../../../../../common/api/detection_engine'; + +export default { + component: RelatedIntegrationsReadOnly, + title: + 'Rule Management/Prebuilt Rules/Upgrade Flyout/ThreeWayDiff/FieldReadOnly/related_integrations', +}; + +const mockedIntegrationsData = [ + { + package_name: 'endpoint', + package_title: 'Elastic Defend', + latest_package_version: '8.15.1', + installed_package_version: '8.16.0-prerelease.1', + is_installed: true, + is_enabled: false, + }, +]; + +function MockRelatedIntegrationsData({ children }: { children: React.ReactNode }) { + const queryClient = useQueryClient(); + + queryClient.setQueryData(['integrations'], mockedIntegrationsData); + + return <>{children}; +} + +interface TemplateProps { + finalDiffableRule: Partial; +} + +const Template: Story = (args) => { + return ( + + + + + + ); +}; + +export const Default = Template.bind({}); + +Default.args = { + finalDiffableRule: { + related_integrations: [{ package: 'endpoint', version: '^8.2.0' }], + }, +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/related_integrations/related_integrations.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/related_integrations/related_integrations.tsx new file mode 100644 index 0000000000000..6e6c0708a0bb4 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/related_integrations/related_integrations.tsx @@ -0,0 +1,29 @@ +/* + * 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 { EuiDescriptionList } from '@elastic/eui'; +import * as ruleDetailsI18n from '../../../../translations'; +import { RelatedIntegrationsDescription } from '../../../../../../../../detections/components/rules/related_integrations/integrations_description'; +import type { RelatedIntegrationArray } from '../../../../../../../../../common/api/detection_engine'; + +interface RelatedIntegrationsReadOnly { + relatedIntegrations: RelatedIntegrationArray; +} + +export function RelatedIntegrationsReadOnly({ relatedIntegrations }: RelatedIntegrationsReadOnly) { + return ( + , + }, + ]} + /> + ); +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/required_fields/required_fields.stories.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/required_fields/required_fields.stories.tsx new file mode 100644 index 0000000000000..2957d8ff7ceaf --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/required_fields/required_fields.stories.tsx @@ -0,0 +1,40 @@ +/* + * 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 type { Story } from '@storybook/react'; +import { RequiredFieldsReadOnly } from './required_fields'; +import { FieldReadOnly } from '../../field_readonly'; +import type { DiffableAllFields } from '../../../../../../../../../common/api/detection_engine'; + +export default { + component: RequiredFieldsReadOnly, + title: 'Rule Management/Prebuilt Rules/Upgrade Flyout/ThreeWayDiff/FieldReadOnly/required_fields', +}; + +interface TemplateProps { + finalDiffableRule: Partial; +} + +const Template: Story = (args) => { + return ( + + ); +}; + +export const Default = Template.bind({}); + +Default.args = { + finalDiffableRule: { + required_fields: [ + { name: 'event.kind', type: 'keyword', ecs: true }, + { name: 'event.module', type: 'keyword', ecs: true }, + ], + }, +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/required_fields/required_fields.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/required_fields/required_fields.tsx new file mode 100644 index 0000000000000..4d60acc8d5f15 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/required_fields/required_fields.tsx @@ -0,0 +1,29 @@ +/* + * 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 { EuiDescriptionList } from '@elastic/eui'; +import * as ruleDetailsI18n from '../../../../translations'; +import type { RequiredFieldArray } from '../../../../../../../../../common/api/detection_engine'; +import { RequiredFields } from '../../../../rule_definition_section'; + +interface RequiredFieldsReadOnlyProps { + requiredFields: RequiredFieldArray; +} + +export function RequiredFieldsReadOnly({ requiredFields }: RequiredFieldsReadOnlyProps) { + return ( + , + }, + ]} + /> + ); +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/risk_score_mapping/risk_score_mapping.stories.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/risk_score_mapping/risk_score_mapping.stories.tsx new file mode 100644 index 0000000000000..76775d6da4586 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/risk_score_mapping/risk_score_mapping.stories.tsx @@ -0,0 +1,39 @@ +/* + * 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 type { Story } from '@storybook/react'; +import { FieldReadOnly } from '../../field_readonly'; +import type { DiffableAllFields } from '../../../../../../../../../common/api/detection_engine'; +import { RiskScoreMappingReadOnly } from './risk_score_mapping'; + +export default { + component: RiskScoreMappingReadOnly, + title: + 'Rule Management/Prebuilt Rules/Upgrade Flyout/ThreeWayDiff/FieldReadOnly/risk_score_mapping', +}; + +interface TemplateProps { + finalDiffableRule: Partial; +} + +const Template: Story = (args) => { + return ( + + ); +}; + +export const Default = Template.bind({}); + +Default.args = { + finalDiffableRule: { + risk_score_mapping: [{ field: 'event.risk_score', operator: 'equals', value: '' }], + }, +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/risk_score_mapping/risk_score_mapping.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/risk_score_mapping/risk_score_mapping.tsx new file mode 100644 index 0000000000000..5581bd588a79e --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/risk_score_mapping/risk_score_mapping.tsx @@ -0,0 +1,30 @@ +/* + * 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 { requiredOptional } from '@kbn/zod-helpers'; +import { EuiDescriptionList } from '@elastic/eui'; +import type { RiskScoreMapping } from '../../../../../../../../../common/api/detection_engine'; +import * as ruleDetailsI18n from '../../../../translations'; +import { RiskScoreMappingItem } from '../../../../rule_about_section'; + +export interface RiskScoreMappingReadProps { + riskScoreMapping: RiskScoreMapping; +} + +export const RiskScoreMappingReadOnly = ({ riskScoreMapping }: RiskScoreMappingReadProps) => { + const listItems = riskScoreMapping + .filter((riskScoreMappingItem) => riskScoreMappingItem.field !== '') + .map((riskScoreMappingItem, index) => ({ + title: index === 0 ? ruleDetailsI18n.RISK_SCORE_MAPPING_FIELD_LABEL : '', + description: ( + + ), + })); + + return ; +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/severity_mapping/severity_mapping.stories.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/severity_mapping/severity_mapping.stories.tsx new file mode 100644 index 0000000000000..92d7b95b17c2b --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/severity_mapping/severity_mapping.stories.tsx @@ -0,0 +1,52 @@ +/* + * 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 type { Story } from '@storybook/react'; +import { FieldReadOnly } from '../../field_readonly'; +import type { DiffableAllFields } from '../../../../../../../../../common/api/detection_engine'; +import { SeverityMappingReadOnly } from './severity_mapping'; + +export default { + component: SeverityMappingReadOnly, + title: + 'Rule Management/Prebuilt Rules/Upgrade Flyout/ThreeWayDiff/FieldReadOnly/severity_mapping', +}; + +interface TemplateProps { + finalDiffableRule: Partial; +} + +const Template: Story = (args) => { + return ( + + ); +}; + +export const Default = Template.bind({}); + +Default.args = { + finalDiffableRule: { + severity_mapping: [ + { + field: 'event.severity', + operator: 'equals', + severity: 'low', + value: 'LOW', + }, + { + field: 'google_workspace.alert.metadata.severity', + operator: 'equals', + severity: 'high', + value: 'VERY HIGH', + }, + ], + }, +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/severity_mapping/severity_mapping.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/severity_mapping/severity_mapping.tsx new file mode 100644 index 0000000000000..4d7b478e00f9e --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/severity_mapping/severity_mapping.tsx @@ -0,0 +1,27 @@ +/* + * 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 { EuiDescriptionList } from '@elastic/eui'; +import type { SeverityMapping } from '../../../../../../../../../common/api/detection_engine'; +import * as ruleDetailsI18n from '../../../../translations'; +import { SeverityMappingItem } from '../../../../rule_about_section'; + +export interface SeverityMappingReadOnlyProps { + severityMapping: SeverityMapping; +} + +export const SeverityMappingReadOnly = ({ severityMapping }: SeverityMappingReadOnlyProps) => { + const listItems = severityMapping + .filter((severityMappingItem) => severityMappingItem.field !== '') + .map((severityMappingItem, index) => ({ + title: index === 0 ? ruleDetailsI18n.SEVERITY_MAPPING_FIELD_LABEL : '', + description: , + })); + + return ; +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/threat/threat.stories.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/threat/threat.stories.tsx new file mode 100644 index 0000000000000..4909174dcb577 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/threat/threat.stories.tsx @@ -0,0 +1,61 @@ +/* + * 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 type { Story } from '@storybook/react'; +import { FieldReadOnly } from '../../field_readonly'; +import type { DiffableAllFields } from '../../../../../../../../../common/api/detection_engine'; +import { ThreatReadOnly } from './threat'; + +export default { + component: ThreatReadOnly, + title: 'Rule Management/Prebuilt Rules/Upgrade Flyout/ThreeWayDiff/FieldReadOnly/threat', +}; + +interface TemplateProps { + finalDiffableRule: Partial; +} + +const Template: Story = (args) => { + return ( + + ); +}; + +export const Default = Template.bind({}); + +Default.args = { + finalDiffableRule: { + threat: [ + { + framework: 'MITRE ATT&CK', + tactic: { + id: 'TA0006', + name: 'Credential Access', + reference: 'https://attack.mitre.org/tactics/TA0006/', + }, + technique: [ + { + id: 'T1003', + name: 'OS Credential Dumping', + reference: 'https://attack.mitre.org/techniques/T1003/', + subtechnique: [ + { + id: 'T1003.001', + name: 'LSASS Memory', + reference: 'https://attack.mitre.org/techniques/T1003/001/', + }, + ], + }, + ], + }, + ], + }, +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/threat/threat.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/threat/threat.tsx new file mode 100644 index 0000000000000..587d9addab628 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/threat/threat.tsx @@ -0,0 +1,29 @@ +/* + * 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 { EuiDescriptionList } from '@elastic/eui'; +import type { Threats } from '@kbn/securitysolution-io-ts-alerting-types'; +import * as ruleDetailsI18n from '../../../../translations'; +import { Threat } from '../../../../rule_about_section'; + +export interface ThreatReadOnlyProps { + threat: Threats; +} + +export const ThreatReadOnly = ({ threat }: ThreatReadOnlyProps) => { + return ( + , + }, + ]} + /> + ); +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/threat_index/threat_index.stories.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/threat_index/threat_index.stories.tsx new file mode 100644 index 0000000000000..c42fa2e890c52 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/threat_index/threat_index.stories.tsx @@ -0,0 +1,38 @@ +/* + * 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 type { Story } from '@storybook/react'; +import { FieldReadOnly } from '../../field_readonly'; +import type { DiffableAllFields } from '../../../../../../../../../common/api/detection_engine'; +import { ThreatIndexReadOnly } from './threat_index'; + +export default { + component: ThreatIndexReadOnly, + title: 'Rule Management/Prebuilt Rules/Upgrade Flyout/ThreeWayDiff/FieldReadOnly/threat_index', +}; + +interface TemplateProps { + finalDiffableRule: Partial; +} + +const Template: Story = (args) => { + return ( + + ); +}; + +export const Default = Template.bind({}); + +Default.args = { + finalDiffableRule: { + threat_index: ['logs-ti_*', 'logs-defend_*'], + }, +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/threat_index/threat_index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/threat_index/threat_index.tsx new file mode 100644 index 0000000000000..23bb2cdfc8bde --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/threat_index/threat_index.tsx @@ -0,0 +1,28 @@ +/* + * 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 { EuiDescriptionList } from '@elastic/eui'; +import * as ruleDetailsI18n from '../../../../translations'; +import { ThreatIndex } from '../../../../rule_definition_section'; + +export interface ThreatIndexReadOnlyProps { + threatIndex: string[]; +} + +export const ThreatIndexReadOnly = ({ threatIndex }: ThreatIndexReadOnlyProps) => { + return ( + , + }, + ]} + /> + ); +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/threat_indicator_path/threat_indicator_path.stories.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/threat_indicator_path/threat_indicator_path.stories.tsx new file mode 100644 index 0000000000000..34f0cdbcac317 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/threat_indicator_path/threat_indicator_path.stories.tsx @@ -0,0 +1,39 @@ +/* + * 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 type { Story } from '@storybook/react'; +import { FieldReadOnly } from '../../field_readonly'; +import type { DiffableAllFields } from '../../../../../../../../../common/api/detection_engine'; +import { ThreatIndicatorPathReadOnly } from './threat_indicator_path'; + +export default { + component: ThreatIndicatorPathReadOnly, + title: + 'Rule Management/Prebuilt Rules/Upgrade Flyout/ThreeWayDiff/FieldReadOnly/threat_indicator_path', +}; + +interface TemplateProps { + finalDiffableRule: Partial; +} + +const Template: Story = (args) => { + return ( + + ); +}; + +export const Default = Template.bind({}); + +Default.args = { + finalDiffableRule: { + threat_indicator_path: 'threat.indicator', + }, +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/threat_indicator_path/threat_indicator_path.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/threat_indicator_path/threat_indicator_path.tsx new file mode 100644 index 0000000000000..c05dfbd71e14f --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/threat_indicator_path/threat_indicator_path.tsx @@ -0,0 +1,34 @@ +/* + * 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 { EuiDescriptionList } from '@elastic/eui'; +import * as ruleDetailsI18n from '../../../../translations'; +import { ThreatIndicatorPath } from '../../../../rule_about_section'; + +export interface ThreatIndicatorPathReadOnlyProps { + threatIndicatorPath?: string; +} + +export const ThreatIndicatorPathReadOnly = ({ + threatIndicatorPath, +}: ThreatIndicatorPathReadOnlyProps) => { + if (!threatIndicatorPath) { + return null; + } + + return ( + , + }, + ]} + /> + ); +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/threat_mapping/threat_mapping.stories.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/threat_mapping/threat_mapping.stories.tsx new file mode 100644 index 0000000000000..05d1aaa6cb028 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/threat_mapping/threat_mapping.stories.tsx @@ -0,0 +1,48 @@ +/* + * 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 type { Story } from '@storybook/react'; +import { FieldReadOnly } from '../../field_readonly'; +import type { DiffableAllFields } from '../../../../../../../../../common/api/detection_engine'; +import { ThreatMappingReadOnly } from './threat_mapping'; + +export default { + component: ThreatMappingReadOnly, + title: 'Rule Management/Prebuilt Rules/Upgrade Flyout/ThreeWayDiff/FieldReadOnly/threat_mapping', +}; + +interface TemplateProps { + finalDiffableRule: Partial; +} + +const Template: Story = (args) => { + return ( + + ); +}; + +export const Default = Template.bind({}); + +Default.args = { + finalDiffableRule: { + threat_mapping: [ + { + entries: [ + { + field: 'Endpoint.capabilities', + type: 'mapping', + value: 'Target.dll.pe.description', + }, + ], + }, + ], + }, +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/threat_mapping/threat_mapping.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/threat_mapping/threat_mapping.tsx new file mode 100644 index 0000000000000..37c68b34a5f38 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/threat_mapping/threat_mapping.tsx @@ -0,0 +1,29 @@ +/* + * 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 { EuiDescriptionList } from '@elastic/eui'; +import type { ThreatMapping } from '../../../../../../../../../common/api/detection_engine'; +import { ThreatMapping as ThreatMappingComponent } from '../../../../rule_definition_section'; +import * as ruleDetailsI18n from '../../../../translations'; + +export interface ThreatMappingReadOnlyProps { + threatMapping: ThreatMapping; +} + +export const ThreatMappingReadOnly = ({ threatMapping }: ThreatMappingReadOnlyProps) => { + return ( + , + }, + ]} + /> + ); +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/threat_query/threat_query.stories.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/threat_query/threat_query.stories.tsx new file mode 100644 index 0000000000000..5b59287255bc9 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/threat_query/threat_query.stories.tsx @@ -0,0 +1,72 @@ +/* + * 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 type { Story } from '@storybook/react'; +import { FieldReadOnly } from '../../field_readonly'; +import type { DiffableAllFields } from '../../../../../../../../../common/api/detection_engine'; +import { ThreatQueryReadOnly } from './threat_query'; +import { + dataSourceWithDataView, + dataSourceWithIndexPatterns, + inlineKqlQuery, + mockDataView, +} from '../../storybook/mocks'; +import { ThreeWayDiffStorybookProviders } from '../../storybook/three_way_diff_storybook_providers'; + +export default { + component: ThreatQueryReadOnly, + title: 'Rule Management/Prebuilt Rules/Upgrade Flyout/ThreeWayDiff/FieldReadOnly/threat_query', +}; + +interface TemplateProps { + finalDiffableRule: Partial; + kibanaServicesMock?: Record; +} + +const Template: Story = (args) => { + return ( + + + + ); +}; + +export const ThreatQueryWithIndexPatterns = Template.bind({}); + +ThreatQueryWithIndexPatterns.args = { + finalDiffableRule: { + threat_query: inlineKqlQuery, + data_source: dataSourceWithIndexPatterns, + }, + kibanaServicesMock: { + data: { + dataViews: { + create: async () => mockDataView(), + }, + }, + }, +}; + +export const ThreatQueryWithDataView = Template.bind({}); + +ThreatQueryWithDataView.args = { + finalDiffableRule: { + threat_query: inlineKqlQuery, + data_source: dataSourceWithDataView, + }, + kibanaServicesMock: { + data: { + dataViews: { + get: async () => mockDataView(), + }, + }, + }, +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/threat_query/threat_query.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/threat_query/threat_query.tsx new file mode 100644 index 0000000000000..6092fd8a0e0ce --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/threat_query/threat_query.tsx @@ -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 type { + DiffableAllFields, + InlineKqlQuery, +} from '../../../../../../../../../common/api/detection_engine'; +import * as ruleDetailsI18n from '../../../../translations'; +import * as descriptionStepI18n from '../../../../../../../rule_creation_ui/components/description_step/translations'; +import { InlineKqlQueryReadOnly } from '../kql_query/inline_kql_query'; + +const i18nLabels = { + query: descriptionStepI18n.THREAT_QUERY_LABEL, + language: ruleDetailsI18n.THREAT_QUERY_LANGUAGE_LABEL, + filters: ruleDetailsI18n.THREAT_FILTERS_FIELD_LABEL, +}; + +export interface ThreatQueryReadOnlyProps { + threatQuery: InlineKqlQuery; + dataSource: DiffableAllFields['data_source']; +} + +export const ThreatQueryReadOnly = ({ threatQuery, dataSource }: ThreatQueryReadOnlyProps) => { + return ( + + ); +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/storybook/mocks.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/storybook/mocks.ts new file mode 100644 index 0000000000000..2b21558e9b0ca --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/storybook/mocks.ts @@ -0,0 +1,105 @@ +/* + * 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 { FieldFormatsStartCommon } from '@kbn/field-formats-plugin/common'; +import { DataView } from '@kbn/data-views-plugin/common'; +import { DataSourceType, KqlQueryType } from '../../../../../../../../common/api/detection_engine'; +import type { + DiffableAllFields, + SavedKqlQuery, +} from '../../../../../../../../common/api/detection_engine'; + +export const filters = [ + { + meta: { + disabled: false, + negate: false, + alias: null, + index: 'logs-*', + key: '@timestamp', + field: '@timestamp', + value: 'exists', + type: 'exists', + }, + query: { + exists: { + field: '@timestamp', + }, + }, + $state: { + store: 'appState', + }, + }, +]; + +export const savedQueryResponse = { + id: 'fake-saved-query-id', + attributes: { + title: 'Fake Saved Query', + description: '', + query: { + query: 'file.path: "/etc/passwd" and event.action: "modification"', + language: 'kuery', + }, + filters, + }, + namespaces: ['default'], +}; + +export const inlineKqlQuery: DiffableAllFields['kql_query'] = { + type: KqlQueryType.inline_query, + query: 'event.action: "user_login" and source.ip: "192.168.1.100"', + language: 'kuery', + filters, +}; + +export const savedKqlQuery: SavedKqlQuery = { + type: KqlQueryType.saved_query, + saved_query_id: 'fake-saved-query-id', +}; + +export const eqlQuery: DiffableAllFields['eql_query'] = { + query: 'process where process.name == "powershell.exe" and process.args : "* -EncodedCommand *"', + language: 'eql', + filters, +}; + +export const dataSourceWithIndexPatterns: DiffableAllFields['data_source'] = { + type: DataSourceType.index_patterns, + index_patterns: ['logs-*'], +}; + +export const dataSourceWithDataView: DiffableAllFields['data_source'] = { + type: DataSourceType.data_view, + data_view_id: 'logs-*', +}; + +type DataViewDeps = ConstructorParameters[0]; + +export function mockDataView(spec: Partial = {}): DataView { + const dataView = new DataView({ + spec: { + title: 'logs-*', + fields: { + '@timestamp': { + name: '@timestamp', + type: 'date', + searchable: true, + aggregatable: true, + }, + }, + ...spec, + }, + fieldFormats: { + getDefaultInstance: () => ({ + toJSON: () => ({}), + }), + } as unknown as FieldFormatsStartCommon, + }); + + return dataView; +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/storybook/three_way_diff_storybook_providers.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/storybook/three_way_diff_storybook_providers.tsx new file mode 100644 index 0000000000000..483624b6fb408 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/storybook/three_way_diff_storybook_providers.tsx @@ -0,0 +1,86 @@ +/* + * 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 { configureStore } from '@reduxjs/toolkit'; +import { merge } from 'lodash'; +import { Subject } from 'rxjs'; +import { Provider as ReduxStoreProvider } from 'react-redux'; +import type { CoreStart } from '@kbn/core/public'; +import { createKibanaReactContext } from '@kbn/kibana-react-plugin/public'; +import { ReactQueryClientProvider } from '../../../../../../../common/containers/query_client/query_client_provider'; + +function createKibanaServicesMock(overrides?: Partial) { + const baseMock = { + data: { + dataViews: { + create: async () => {}, + get: async () => {}, + }, + }, + http: { + get: async () => {}, + basePath: { + get: () => '', + }, + }, + notifications: { + toasts: { + addError: () => {}, + addSuccess: () => {}, + addWarning: () => {}, + remove: () => {}, + }, + }, + settings: { + client: { + get: (key: string, defaultOverride?: unknown) => defaultOverride, + get$: () => new Subject(), + set: () => {}, + }, + }, + uiSettings: {}, + }; + + return merge(baseMock, overrides); +} + +function createMockStore() { + const store = configureStore({ + reducer: { + app: () => ({ + enableExperimental: { + prebuiltRulesCustomizationEnabled: true, + }, + }), + }, + }); + + return store; +} + +interface StorybookProvidersProps { + children: React.ReactNode; + kibanaServicesMock?: Record; +} + +export function ThreeWayDiffStorybookProviders({ + children, + kibanaServicesMock, +}: StorybookProvidersProps) { + const KibanaReactContext = createKibanaReactContext(createKibanaServicesMock(kibanaServicesMock)); + + const store = createMockStore(); + + return ( + + + {children} + + + ); +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/translations.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/translations.ts index a5fab42457e44..89c22a285e327 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/translations.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/translations.ts @@ -356,3 +356,38 @@ export const CUSTOMIZED_PREBUILT_RULE_LABEL = i18n.translate( defaultMessage: 'Customized Elastic rule', } ); + +export const QUERY_LANGUAGE_LABEL = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDetails.queryLanguageLabel', + { + defaultMessage: 'Custom query language', + } +); + +export const THREAT_QUERY_LANGUAGE_LABEL = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDetails.threatQueryLanguageLabel', + { + defaultMessage: 'Indicator index query language', + } +); + +export const SAVED_QUERY_LANGUAGE_LABEL = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDetails.savedQueryLanguageLabel', + { + defaultMessage: 'Saved query language', + } +); + +export const KUERY_LANGUAGE_LABEL = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDetails.kqlLanguageLabel', + { + defaultMessage: 'KQL', + } +); + +export const LUCENE_LANGUAGE_LABEL = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDetails.luceneLanguageLabel', + { + defaultMessage: 'Lucene', + } +); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/use_default_index_pattern.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/use_default_index_pattern.tsx new file mode 100644 index 0000000000000..3482df562bac0 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/use_default_index_pattern.tsx @@ -0,0 +1,27 @@ +/* + * 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 { useKibana } from '../../../../common/lib/kibana/kibana_react'; +import { DEFAULT_INDEX_KEY, DEFAULT_INDEX_PATTERN } from '../../../../../common/constants'; +import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; + +/** + * Gets the default index pattern for cases when rule has neither index patterns or data view. + * First checks the config value. If it's not present falls back to the hardcoded default value. + */ +export function useDefaultIndexPattern() { + const { services } = useKibana(); + const isPrebuiltRulesCustomizationEnabled = useIsExperimentalFeatureEnabled( + 'prebuiltRulesCustomizationEnabled' + ); + + if (isPrebuiltRulesCustomizationEnabled) { + return services.settings.client.get(DEFAULT_INDEX_KEY, DEFAULT_INDEX_PATTERN); + } + + return []; +}