Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Security Solution] ThreeWayDiff UI: Add FieldReadOnly component #191499

Merged
merged 20 commits into from
Sep 9, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export interface RuleUpgradeStatsForReview {
/** Number of installed prebuilt rules available for upgrade (stock + customized) */
num_rules_to_upgrade_total: number;

/** Number of installed prebuilt rules with upgrade conflicts (SOLVABLE or NON_SOLVALBE) */
/** Number of installed prebuilt rules with upgrade conflicts (SOLVABLE or NON_SOLVABLE) */
nikitaindik marked this conversation as resolved.
Show resolved Hide resolved
num_rules_with_conflicts: number;

/** Number of installed prebuilt rules with NON_SOLVABLE upgrade conflicts */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
SCHEDULE_UPGRADE_FIELD_ORDER,
SETUP_UPGRADE_FIELD_ORDER,
} from './constants';
import * as i18n from './translations';

export const getSectionedFieldDiffs = (fields: FieldsGroupDiff[]) => {
const aboutFields = [];
Expand Down Expand Up @@ -57,3 +58,14 @@ 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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ interface SeverityMappingItemProps {
severityMappingItem: SeverityMappingItemType;
}

const SeverityMappingItem = ({ severityMappingItem }: SeverityMappingItemProps) => (
export const SeverityMappingItem = ({ severityMappingItem }: SeverityMappingItemProps) => (
<EuiFlexGroup alignItems="center" gutterSize="s">
<OverrideColumn>
<EuiToolTip
Expand Down Expand Up @@ -132,7 +132,7 @@ interface RiskScoreMappingItemProps {
riskScoreMappingItem: RiskScoreMappingItemType;
}

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

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

interface ThreatIndicatorPathProps {
threatIndicatorPath: string;
}

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,13 @@ import {
queryStyles,
useRequiredFieldsStyles,
} from './rule_definition_section.styles';
import { getQueryLanguageLabel } from './helpers';

interface SavedQueryNameProps {
savedQueryName: string;
}

const SavedQueryName = ({ savedQueryName }: SavedQueryNameProps) => (
export const SavedQueryName = ({ savedQueryName }: SavedQueryNameProps) => (
<EuiText size="s" data-test-subj="savedQueryNamePropertyValue">
{savedQueryName}
</EuiText>
Expand All @@ -75,7 +76,12 @@ 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 { indexPattern } = useRuleIndexPattern({
Expand Down Expand Up @@ -104,7 +110,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 (
<div data-test-subj={dataTestSubj} className={styles.content}>
Expand All @@ -117,15 +123,15 @@ interface IndexProps {
index: string[];
}

const Index = ({ index }: IndexProps) => (
export const Index = ({ index }: IndexProps) => (
<BadgeList badges={index} data-test-subj="indexPropertyValue" />
);

interface DataViewIdProps {
dataViewId: string;
}

const DataViewId = ({ dataViewId }: DataViewIdProps) => (
export const DataViewId = ({ dataViewId }: DataViewIdProps) => (
<EuiText size="s" data-test-subj="dataViewIdPropertyValue">
{dataViewId}
</EuiText>
Expand All @@ -135,7 +141,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);
Expand Down Expand Up @@ -191,18 +197,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 <MlJobsDescription jobIds={jobIds} />;
return <MlJobsDescription jobIds={jobIdsArray} />;
}

const relevantJobs = jobs.filter((job) => jobIds.includes(job.id));
const relevantJobs = jobs.filter((job) => jobIdsArray.includes(job.id));

return (
<>
Expand Down Expand Up @@ -251,7 +263,7 @@ interface RequiredFieldsProps {
requiredFields: RequiredFieldArray;
}

const RequiredFields = ({ requiredFields }: RequiredFieldsProps) => {
export const RequiredFields = ({ requiredFields }: RequiredFieldsProps) => {
const styles = useRequiredFieldsStyles();

return (
Expand Down Expand Up @@ -293,15 +305,15 @@ interface ThreatIndexProps {
threatIndex: string[];
}

const ThreatIndex = ({ threatIndex }: ThreatIndexProps) => (
export const ThreatIndex = ({ threatIndex }: ThreatIndexProps) => (
<BadgeList badges={threatIndex} data-test-subj="threatIndexPropertyValue" />
);

interface ThreatMappingProps {
threatMapping: ThreatMappingType;
}

const ThreatMapping = ({ threatMapping }: ThreatMappingProps) => {
export const ThreatMapping = ({ threatMapping }: ThreatMappingProps) => {
const description = threatMapping.reduce<string>(
(accumThreatMaps, threatMap, threatMapIndex, { length: threatMappingLength }) => {
const matches = threatMap.entries.reduce<string>(
Expand Down Expand Up @@ -434,14 +446,28 @@ const prepareDefinitionSectionListItems = (
}

if (savedQuery) {
definitionSectionListItems.push({
title: (
<span data-test-subj="savedQueryNamePropertyTitle">
{descriptionStepI18n.SAVED_QUERY_NAME_LABEL}
</span>
),
description: <SavedQueryName savedQueryName={savedQuery.attributes.title} />,
});
definitionSectionListItems.push(
{
title: (
<span data-test-subj="savedQueryNamePropertyTitle">
{descriptionStepI18n.SAVED_QUERY_NAME_LABEL}
</span>
),
description: <SavedQueryName savedQueryName={savedQuery.attributes.title} />,
},
{
title: (
<span data-test-subj="savedQueryLanguagePropertyTitle">
{i18n.SAVED_QUERY_LANGUAGE_LABEL}
</span>
),
description: (
<span data-test-subj="savedQueryLanguagePropertyValue">
{getQueryLanguageLabel(savedQuery.attributes.query.language)}
</span>
),
}
);

if (savedQuery.attributes.filters) {
definitionSectionListItems.push({
Expand Down Expand Up @@ -508,12 +534,26 @@ const prepareDefinitionSectionListItems = (
description: <Query query={rule.query} data-test-subj="esqlQueryPropertyValue" />,
});
} else {
definitionSectionListItems.push({
title: (
<span data-test-subj="customQueryPropertyTitle">{descriptionStepI18n.QUERY_LABEL}</span>
),
description: <Query query={rule.query} data-test-subj="customQueryPropertyValue" />,
});
definitionSectionListItems.push(
{
title: (
<span data-test-subj="customQueryPropertyTitle">{descriptionStepI18n.QUERY_LABEL}</span>
),
description: <Query query={rule.query} data-test-subj="customQueryPropertyValue" />,
},
{
title: (
<span data-test-subj="customQueryLanguagePropertyTitle">
{i18n.QUERY_LANGUAGE_LABEL}
</span>
),
description: (
<span data-test-subj="customQueryLanguagePropertyValue">
{getQueryLanguageLabel(rule.language || '')}
</span>
),
}
);
}
}

Expand Down Expand Up @@ -542,7 +582,7 @@ const prepareDefinitionSectionListItems = (
),
description: (
<MachineLearningJobList
jobIds={rule.machine_learning_job_id as string[]}
jobIds={rule.machine_learning_job_id}
isInteractive={isInteractive}
/>
),
Expand Down Expand Up @@ -633,6 +673,21 @@ const prepareDefinitionSectionListItems = (
});
}

if ('threat_language' in rule && rule.threat_language) {
definitionSectionListItems.push({
title: (
<span data-test-subj="threatQueryLanguagePropertyTitle">
{i18n.THREAT_QUERY_LANGUAGE_LABEL}
</span>
),
description: (
<span data-test-subj="threatQueryLanguagePropertyValue">
{getQueryLanguageLabel(rule.threat_language)}
</span>
),
});
}

if ('new_terms_fields' in rule && rule.new_terms_fields && rule.new_terms_fields.length > 0) {
definitionSectionListItems.push({
title: (
Expand Down
Original file line number Diff line number Diff line change
@@ -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 <DataSourceReadOnly dataSource={finalDiffableRule.data_source} />;
case 'eql_query':
return (
<EqlQueryReadOnly
eqlQuery={finalDiffableRule.eql_query}
dataSource={finalDiffableRule.data_source}
/>
);
case 'esql_query':
return <EsqlQueryReadOnly esqlQuery={finalDiffableRule.esql_query} />;
case 'kql_query':
return (
<KqlQueryReadOnly
kqlQuery={finalDiffableRule.kql_query}
dataSource={finalDiffableRule.data_source}
ruleType={finalDiffableRule.type}
/>
);
case 'machine_learning_job_id':
return (
<MachineLearningJobIdReadOnly
machineLearningJobId={finalDiffableRule.machine_learning_job_id}
/>
);
case 'related_integrations':
return (
<RelatedIntegrationsReadOnly relatedIntegrations={finalDiffableRule.related_integrations} />
);
case 'required_fields':
return <RequiredFieldsReadOnly requiredFields={finalDiffableRule.required_fields} />;
case 'risk_score_mapping':
return <RiskScoreMappingReadOnly riskScoreMapping={finalDiffableRule.risk_score_mapping} />;
case 'severity_mapping':
return <SeverityMappingReadOnly severityMapping={finalDiffableRule.severity_mapping} />;
case 'threat':
return <ThreatReadOnly threat={finalDiffableRule.threat} />;
case 'threat_index':
return <ThreatIndexReadOnly threatIndex={finalDiffableRule.threat_index} />;
case 'threat_indicator_path':
return (
<ThreatIndicatorPathReadOnly
threatIndicatorPath={finalDiffableRule.threat_indicator_path}
/>
);
case 'threat_mapping':
return <ThreatMappingReadOnly threatMapping={finalDiffableRule.threat_mapping} />;
case 'threat_query':
return (
<ThreatQueryReadOnly
threatQuery={finalDiffableRule.threat_query}
dataSource={finalDiffableRule.data_source}
/>
);
default:
return null;
}
}
Loading