From 39a244c77c278f4fe6d1bdbdc7fa37f608a8153e Mon Sep 17 00:00:00 2001 From: Davis Plumlee Date: Tue, 5 Mar 2024 16:41:19 -0500 Subject: [PATCH 1/6] updates openai files before script run --- .../model/rule_schema/rule_schemas.schema.yaml | 9 ++++----- .../components/markdown_editor/editor.tsx | 6 ++++-- .../components/markdown_editor/eui_form.tsx | 4 +++- .../step_about_rule_details/index.test.tsx | 6 +++--- .../components/step_about_rule/default_value.ts | 1 + .../components/step_about_rule/index.test.tsx | 2 ++ .../components/step_about_rule/index.tsx | 12 ++++++++++++ .../components/step_about_rule/schema.tsx | 17 +++++++++++++++++ .../components/step_about_rule/translations.ts | 7 +++++++ .../pages/rule_creation/helpers.ts | 3 +++ .../components/rules_table/__mocks__/mock.ts | 1 + .../pages/detection_engine/rules/types.ts | 1 + .../pages/detection_engine/rules/utils.ts | 1 + 13 files changed, 59 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_schemas.schema.yaml b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_schemas.schema.yaml index 22308557c3aaa..d3a09d8355727 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_schemas.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_schemas.schema.yaml @@ -128,6 +128,8 @@ components: $ref: './common_attributes.schema.yaml#/components/schemas/MaxSignals' threat: $ref: './common_attributes.schema.yaml#/components/schemas/ThreatArray' + setup: + $ref: './common_attributes.schema.yaml#/components/schemas/SetupGuide' BaseCreateProps: x-inline: true @@ -174,7 +176,7 @@ components: revision: type: integer minimum: 0 - # NOTE: For now, Related Integrations, Required Fields and Setup Guide are + # NOTE: For now, Related Integrations and Required Fields are # supported for prebuilt rules only. We don't want to allow users to edit these 3 # fields via the API. If we added them to baseParams.defaultable, they would # become a part of the request schema as optional fields. This is why we add them @@ -183,8 +185,6 @@ components: $ref: './common_attributes.schema.yaml#/components/schemas/RelatedIntegrationArray' required_fields: $ref: './common_attributes.schema.yaml#/components/schemas/RequiredFieldArray' - setup: - $ref: './common_attributes.schema.yaml#/components/schemas/SetupGuide' execution_summary: $ref: '../../rule_monitoring/model/execution_summary.schema.yaml#/components/schemas/RuleExecutionSummary' required: @@ -198,7 +198,6 @@ components: - revision - related_integrations - required_fields - - setup SharedCreateProps: x-inline: true @@ -279,7 +278,7 @@ components: $ref: './specific_attributes/eql_attributes.schema.yaml#/components/schemas/TiebreakerField' timestamp_field: $ref: './specific_attributes/eql_attributes.schema.yaml#/components/schemas/TimestampField' - + EqlRuleCreateFields: allOf: - $ref: '#/components/schemas/EqlRequiredFields' diff --git a/x-pack/plugins/security_solution/public/common/components/markdown_editor/editor.tsx b/x-pack/plugins/security_solution/public/common/components/markdown_editor/editor.tsx index 64d289cd65f3e..2f439c55a7d1c 100644 --- a/x-pack/plugins/security_solution/public/common/components/markdown_editor/editor.tsx +++ b/x-pack/plugins/security_solution/public/common/components/markdown_editor/editor.tsx @@ -31,6 +31,7 @@ interface MarkdownEditorProps { height?: number; autoFocusDisabled?: boolean; setIsMarkdownInvalid: (value: boolean) => void; + includePlugins?: boolean; } type EuiMarkdownEditorRef = ElementRef; @@ -52,6 +53,7 @@ const MarkdownEditorComponent = forwardRef { @@ -73,8 +75,8 @@ const MarkdownEditorComponent = forwardRef { - return uiPlugins({ insightsUpsellingMessage }); - }, [insightsUpsellingMessage]); + return includePlugins ? uiPlugins({ insightsUpsellingMessage }) : undefined; + }, [insightsUpsellingMessage, includePlugins]); // @ts-expect-error update types useImperativeHandle(ref, () => { diff --git a/x-pack/plugins/security_solution/public/common/components/markdown_editor/eui_form.tsx b/x-pack/plugins/security_solution/public/common/components/markdown_editor/eui_form.tsx index dc157a85afa2b..8fdbc3559bbc4 100644 --- a/x-pack/plugins/security_solution/public/common/components/markdown_editor/eui_form.tsx +++ b/x-pack/plugins/security_solution/public/common/components/markdown_editor/eui_form.tsx @@ -23,6 +23,7 @@ type MarkdownEditorFormProps = EuiMarkdownEditorProps & { idAria: string; isDisabled?: boolean; bottomRightContent?: React.ReactNode; + includePlugins?: boolean; }; /* eslint-enable react/no-unused-prop-types */ @@ -34,7 +35,7 @@ const BottomContentWrapper = styled(EuiFlexGroup)` export const MarkdownEditorForm = React.memo( forwardRef( - ({ id, field, dataTestSubj, idAria, bottomRightContent }, ref) => { + ({ id, field, dataTestSubj, idAria, bottomRightContent, includePlugins }, ref) => { const { isInvalid, errorMessage } = getFieldValidityAndErrorMessage(field); const [isMarkdownInvalid, setIsMarkdownInvalid] = useState(false); @@ -58,6 +59,7 @@ export const MarkdownEditorForm = React.memo( value={field.value as string} data-test-subj={`${dataTestSubj}-markdown-editor`} setIsMarkdownInvalid={setIsMarkdownInvalid} + includePlugins={includePlugins} /> {bottomRightContent && ( diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/step_about_rule_details/index.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/step_about_rule_details/index.test.tsx index 953e75ab5ceda..8650c4fbec29e 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/step_about_rule_details/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/step_about_rule_details/index.test.tsx @@ -212,7 +212,7 @@ describe('StepAboutRuleToggleDetails', () => { stepDataDetails={{ note: stepDataMock.note, description: stepDataMock.description, - setup: stepDataMock.note, // TODO: Update to mockRule.setup once supported in UI (and mock can be updated) + setup: stepDataMock.setup, }} stepData={stepDataMock} rule={mockRule('mocked-rule-id')} @@ -234,7 +234,7 @@ describe('StepAboutRuleToggleDetails', () => { stepDataDetails={{ note: stepDataMock.note, description: stepDataMock.description, - setup: stepDataMock.note, // TODO: Update to mockRule.setup once supported in UI (and mock can be updated) + setup: stepDataMock.setup, }} stepData={stepDataMock} rule={mockRule('mocked-rule-id')} @@ -261,7 +261,7 @@ describe('StepAboutRuleToggleDetails', () => { stepDataDetails={{ note: stepDataMock.note, description: stepDataMock.description, - setup: stepDataMock.note, // TODO: Update to mockRule.setup once supported in UI (and mock can be updated) + setup: stepDataMock.setup, }} stepData={stepDataMock} rule={mockRule('mocked-rule-id')} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_about_rule/default_value.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_about_rule/default_value.ts index 91057eb3ff5f8..26f842384ef25 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_about_rule/default_value.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_about_rule/default_value.ts @@ -33,4 +33,5 @@ export const stepAboutDefaultValue: AboutStepRule = { timestampOverride: '', threat: threatDefault, note: '', + setup: '', }; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_about_rule/index.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_about_rule/index.test.tsx index d654eaef9cca7..dc3fc5645b138 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_about_rule/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_about_rule/index.test.tsx @@ -269,6 +269,7 @@ describe('StepAboutRuleComponent', () => { falsePositives: [''], name: 'Test name text', note: '', + setup: '', references: [''], riskScore: { value: 21, mapping: [], isMappingChecked: false }, severity: { value: 'low', mapping: fillEmptySeverityMappings([]), isMappingChecked: false }, @@ -329,6 +330,7 @@ describe('StepAboutRuleComponent', () => { falsePositives: [''], name: 'Test name text', note: '', + setup: '', references: [''], riskScore: { value: 80, mapping: [], isMappingChecked: false }, severity: { value: 'low', mapping: fillEmptySeverityMappings([]), isMappingChecked: false }, diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_about_rule/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_about_rule/index.tsx index 85f5284f83452..99589f93d431e 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_about_rule/index.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_about_rule/index.tsx @@ -247,6 +247,18 @@ const StepAboutRuleComponent: FC = ({ }} /> + + = { ), labelAppend: OptionalFieldLabel, }, + setup: { + type: FIELD_TYPES.TEXTAREA, + label: i18n.translate( + 'xpack.securitySolution.detectionEngine.createRule.stepAboutRule.setupLabel', + { + defaultMessage: 'Setup guide', + } + ), + helpText: i18n.translate( + 'xpack.securitySolution.detectionEngine.createRule.stepAboutRule.setupHelpText', + { + defaultMessage: + 'This guide will appear on the rule details page and in timelines (as notes) created from detection alerts generated by this rule.', + } + ), + labelAppend: OptionalFieldLabel, + }, }; const threatIndicatorPathRequiredSchemaValue = { diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_about_rule/translations.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_about_rule/translations.ts index 007cf4d9dd4c6..d07fe22a8ed7b 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_about_rule/translations.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_about_rule/translations.ts @@ -90,3 +90,10 @@ export const ADD_RULE_NOTE_HELP_TEXT = i18n.translate( defaultMessage: 'Add rule investigation guide...', } ); + +export const ADD_RULE_SETUP_HELP_TEXT = i18n.translate( + 'xpack.securitySolution.detectionEngine.createRule.stepAboutrule.setupHelpText', + { + defaultMessage: 'Add rule setup guide...', + } +); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.ts index 924dc4a62fd70..8c7a81f7a6ed9 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.ts @@ -557,6 +557,7 @@ export const formatAboutStepData = ( isAssociatedToEndpointList, isBuildingBlock, note, + setup, ruleNameOverride, threatIndicatorPath, timestampOverride, @@ -610,8 +611,10 @@ export const formatAboutStepData = ( timestamp_override: timestampOverride !== '' ? timestampOverride : undefined, timestamp_override_fallback_disabled: timestampOverrideFallbackDisabled, ...(!isEmpty(note) ? { note } : {}), + ...(!isEmpty(setup) ? { setup } : {}), ...rest, }; + console.log(resp); return resp; }; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/__mocks__/mock.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/__mocks__/mock.ts index 4c23c14871067..abfabbf17aaec 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/__mocks__/mock.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/__mocks__/mock.ts @@ -197,6 +197,7 @@ export const mockAboutStepRule = (): AboutStepRule => ({ tags: ['tag1', 'tag2'], threat: getThreatMock(), note: '# this is some markdown documentation', + setup: '# this is some setup markdown documentation', investigationFields: ['foo', 'bar'], }); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/types.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/types.ts index f57184a3a490b..d6cf3666bffff 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/types.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/types.ts @@ -101,6 +101,7 @@ export interface AboutStepRule { threatIndicatorPath?: string; threat: Threats; note: string; + setup: SetupGuide; } export interface AboutStepRuleDetails { diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/utils.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/utils.ts index 9e54856b7b28c..565180217f842 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/utils.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/utils.ts @@ -93,6 +93,7 @@ export const stepAboutDefaultValue: AboutStepRule = { timestampOverride: '', threat: threatDefault, note: '', + setup: '', threatIndicatorPath: undefined, timestampOverrideFallbackDisabled: undefined, }; From f8100db8c232527a36da88a42e28a948f71cd25f Mon Sep 17 00:00:00 2001 From: Davis Plumlee Date: Wed, 6 Mar 2024 10:27:04 -0500 Subject: [PATCH 2/6] runs script and updates api --- .../model/rule_schema/rule_schemas.gen.ts | 4 ++-- .../rule_creation_ui/pages/rule_creation/helpers.ts | 1 - .../pages/detection_engine/rules/helpers.tsx | 11 ++++++----- .../rule_management/logic/crud/update_rules.ts | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_schemas.gen.ts b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_schemas.gen.ts index 9d27297d11bbe..d7a8b83ec28f4 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_schemas.gen.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_schemas.gen.ts @@ -52,12 +52,12 @@ import { RuleReferenceArray, MaxSignals, ThreatArray, + SetupGuide, RuleObjectId, RuleSignatureId, IsRuleImmutable, RelatedIntegrationArray, RequiredFieldArray, - SetupGuide, RuleQuery, IndexPatternArray, DataViewId, @@ -134,6 +134,7 @@ export const BaseDefaultableFields = z.object({ references: RuleReferenceArray.optional(), max_signals: MaxSignals.optional(), threat: ThreatArray.optional(), + setup: SetupGuide.optional(), }); export type BaseCreateProps = z.infer; @@ -162,7 +163,6 @@ export const ResponseFields = z.object({ revision: z.number().int().min(0), related_integrations: RelatedIntegrationArray, required_fields: RequiredFieldArray, - setup: SetupGuide, execution_summary: RuleExecutionSummary.optional(), }); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.ts index 8c7a81f7a6ed9..e0e311fe55550 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.ts @@ -614,7 +614,6 @@ export const formatAboutStepData = ( ...(!isEmpty(setup) ? { setup } : {}), ...rest, }; - console.log(resp); return resp; }; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx index 96a3b17a77871..574397c80e767 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx @@ -222,7 +222,7 @@ export const getHumanizedDuration = (from: string, interval: string): string => }; export const getAboutStepsData = (rule: RuleResponse, detailsView: boolean): AboutStepRule => { - const { name, description, note } = determineDetailsValue(rule, detailsView); + const { name, description, note, setup } = determineDetailsValue(rule, detailsView); const { author, building_block_type: buildingBlockType, @@ -272,6 +272,7 @@ export const getAboutStepsData = (rule: RuleResponse, detailsView: boolean): Abo investigationFields: investigationFields?.field_names ?? [], threat: threat as Threats, threatIndicatorPath, + setup, }; }; @@ -296,13 +297,13 @@ export const fillEmptySeverityMappings = (mappings: SeverityMapping): SeverityMa export const determineDetailsValue = ( rule: RuleResponse, detailsView: boolean -): Pick => { - const { name, description, note } = rule; +): Pick => { + const { name, description, note, setup } = rule; if (detailsView) { - return { name: '', description: '', note: '' }; + return { name: '', description: '', note: '', setup: '' }; } - return { name, description, note: note ?? '' }; + return { name, description, setup, note: note ?? '' }; }; export const getModifiedAboutDetailsData = (rule: RuleResponse): AboutStepRuleDetails => ({ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/update_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/update_rules.ts index 4ffea7d55c9a9..a4fcd4797b75b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/update_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/update_rules.ts @@ -62,7 +62,7 @@ export const updateRules = async ({ riskScore: ruleUpdate.risk_score, riskScoreMapping: ruleUpdate.risk_score_mapping ?? [], ruleNameOverride: ruleUpdate.rule_name_override, - setup: existingRule.params.setup, + setup: ruleUpdate.setup, severity: ruleUpdate.severity, severityMapping: ruleUpdate.severity_mapping ?? [], threat: ruleUpdate.threat ?? [], From 67688c042d3d0842319d74fb69025c12d4fb7f45 Mon Sep 17 00:00:00 2001 From: Davis Plumlee Date: Wed, 6 Mar 2024 11:40:44 -0500 Subject: [PATCH 3/6] fixes tests --- .../components/rules_table/__mocks__/mock.ts | 4 ++-- .../detection_engine/rules/helpers.test.tsx | 23 ++++++++++++------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/__mocks__/mock.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/__mocks__/mock.ts index abfabbf17aaec..9530daafde534 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/__mocks__/mock.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/__mocks__/mock.ts @@ -81,7 +81,7 @@ export const mockRule = (id: string): SavedQueryRule => ({ meta: { from: '0m' }, related_integrations: [], required_fields: [], - setup: '', + setup: '# this is some setup documentation', severity: 'low', severity_mapping: [], updated_by: 'elastic', @@ -149,7 +149,7 @@ export const mockRuleWithEverything = (id: string): RuleResponse => ({ meta: { from: '0m' }, related_integrations: [], required_fields: [], - setup: '', + setup: '# this is some setup documentation', severity: 'low', severity_mapping: [], updated_by: 'elastic', diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.test.tsx index 2ffedcdc55568..bcb73b1f9edc2 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.test.tsx @@ -146,6 +146,7 @@ describe('rule helpers', () => { timestampOverride: 'event.ingested', timestampOverrideFallbackDisabled: false, investigationFields: [], + setup: '# this is some setup documentation', }; const scheduleRuleStepData = { from: '0s', interval: '5m' }; const ruleActionsStepData = { @@ -156,7 +157,7 @@ describe('rule helpers', () => { const aboutRuleDataDetailsData = { note: '# this is some markdown documentation', description: '24/7', - setup: '', + setup: '# this is some setup documentation', }; expect(defineRuleData).toEqual(defineRuleStepData); @@ -195,18 +196,18 @@ describe('rule helpers', () => { describe('determineDetailsValue', () => { test('returns name, description, and note as empty string if detailsView is true', () => { - const result: Pick = determineDetailsValue( + const result: Pick = determineDetailsValue( mockRuleWithEverything('test-id'), true ); - const expected = { name: '', description: '', note: '' }; + const expected = { name: '', description: '', note: '', setup: '' }; expect(result).toEqual(expected); }); test('returns name, description, and note values if detailsView is false', () => { const mockedRule = mockRuleWithEverything('test-id'); - const result: Pick = determineDetailsValue( + const result: Pick = determineDetailsValue( mockedRule, false ); @@ -214,6 +215,7 @@ describe('rule helpers', () => { name: mockedRule.name, description: mockedRule.description, note: mockedRule.note, + setup: mockedRule.setup, }; expect(result).toEqual(expected); @@ -222,11 +224,16 @@ describe('rule helpers', () => { test('returns note as empty string if property does not exist on rule', () => { const mockedRule = mockRuleWithEverything('test-id'); delete mockedRule.note; - const result: Pick = determineDetailsValue( + const result: Pick = determineDetailsValue( mockedRule, false ); - const expected = { name: mockedRule.name, description: mockedRule.description, note: '' }; + const expected = { + name: mockedRule.name, + description: mockedRule.description, + note: '', + setup: mockedRule.setup, + }; expect(result).toEqual(expected); }); @@ -418,7 +425,7 @@ describe('rule helpers', () => { const aboutRuleDataDetailsData = { note: '# this is some markdown documentation', description: '24/7', - setup: '', + setup: '# this is some setup documentation', }; expect(result).toEqual(aboutRuleDataDetailsData); @@ -431,7 +438,7 @@ describe('rule helpers', () => { const aboutRuleDetailsData = { note: '', description: mockRuleWithoutNote.description, - setup: '', + setup: '# this is some setup documentation', }; expect(result).toEqual(aboutRuleDetailsData); From e3e7bca2363ed7fe70385effa1da8b19b645ea8c Mon Sep 17 00:00:00 2001 From: Davis Plumlee Date: Wed, 6 Mar 2024 15:50:35 -0500 Subject: [PATCH 4/6] updates tests --- .../step_about_rule_details/index.test.tsx | 42 ++++++++++--------- .../components/description_step/helpers.tsx | 18 ++++++++ .../description_step/index.test.tsx | 17 +++++++- .../components/description_step/index.tsx | 4 ++ .../pages/rule_creation/helpers.test.ts | 9 ++++ .../pages/rule_creation/helpers.ts | 2 - .../components/rules_table/__mocks__/mock.ts | 2 +- .../pages/detection_engine/rules/types.ts | 1 + 8 files changed, 71 insertions(+), 24 deletions(-) diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/step_about_rule_details/index.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/step_about_rule_details/index.test.tsx index 8650c4fbec29e..ec39abb61465a 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/step_about_rule_details/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/step_about_rule_details/index.test.tsx @@ -40,7 +40,7 @@ describe('StepAboutRuleToggleDetails', () => { stepDataDetails={{ note: stepDataMock.note, description: stepDataMock.description, - setup: '', + setup: stepDataMock.setup, }} stepData={stepDataMock} rule={mockRule('mocked-rule-id')} @@ -82,28 +82,30 @@ describe('StepAboutRuleToggleDetails', () => { }); describe('note value is empty string', () => { - test('it does not render toggle buttons', () => { + test('it does render toggle buttons if setup is not empty', () => { const mockAboutStepWithoutNote = { ...stepDataMock, note: '', }; - const wrapper = shallow( - + const wrapper = mount( + + + ); - expect(wrapper.find('[data-test-subj="stepAboutDetailsToggle"]').exists()).toBeFalsy(); + expect(wrapper.find(EuiButtonGroup).exists()).toBeTruthy(); + expect(wrapper.find('#details').at(0).prop('isSelected')).toBeTruthy(); + expect(wrapper.find('#setup').at(0).prop('isSelected')).toBeFalsy(); expect(wrapper.find('[data-test-subj="stepAboutDetailsNoteContent"]').exists()).toBeFalsy(); - expect(wrapper.find('[data-test-subj="stepAboutDetailsSetupContent"]').exists()).toBeFalsy(); - expect(wrapper.find('[data-test-subj="stepAboutDetailsContent"]').exists()).toBeTruthy(); }); }); @@ -116,7 +118,7 @@ describe('StepAboutRuleToggleDetails', () => { stepDataDetails={{ note: stepDataMock.note, description: stepDataMock.description, - setup: '', + setup: stepDataMock.setup, }} stepData={stepDataMock} rule={mockRule('mocked-rule-id')} @@ -137,7 +139,7 @@ describe('StepAboutRuleToggleDetails', () => { stepDataDetails={{ note: stepDataMock.note, description: stepDataMock.description, - setup: '', + setup: stepDataMock.setup, }} stepData={stepDataMock} rule={mockRule('mocked-rule-id')} @@ -253,7 +255,7 @@ describe('StepAboutRuleToggleDetails', () => { expect(wrapper.find('[idSelected="setup"]').exists()).toBeTruthy(); }); - test('it displays notes markdown when user toggles to "setup"', () => { + test('it displays setup markdown when user toggles to "setup"', () => { const wrapper = mount( { expect(wrapper.find('EuiButtonGroup[idSelected="setup"]').exists()).toBeTruthy(); expect(wrapper.find('div.euiMarkdownFormat').text()).toEqual( - 'this is some markdown documentation' + 'this is some setup documentation' ); }); }); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/description_step/helpers.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/description_step/helpers.tsx index 5a9dd4f7eb6d3..8681386c1b103 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/description_step/helpers.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/description_step/helpers.tsx @@ -635,3 +635,21 @@ export const buildAlertSuppressionMissingFieldsDescription = ( }, ]; }; + +export const buildSetupDescription = (label: string, setup: string): ListItems[] => { + if (setup.trim() !== '') { + return [ + { + title: label, + description: ( + +
+ {setup} +
+
+ ), + }, + ]; + } + return []; +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/description_step/index.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/description_step/index.test.tsx index ccc9767d742f7..26837ac939b55 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/description_step/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/description_step/index.test.tsx @@ -263,7 +263,7 @@ describe('description_step', () => { mockLicenseService ); - expect(result.length).toEqual(12); + expect(result.length).toEqual(13); }); }); @@ -536,6 +536,21 @@ describe('description_step', () => { }); }); + describe('setup', () => { + test('returns default "setup" description', () => { + const result: ListItems[] = getDescriptionItem( + 'setup', + 'Setup guide', + mockAboutStep, + mockFilterManager, + mockLicenseService + ); + + expect(result[0].title).toEqual('Setup guide'); + expect(React.isValidElement(result[0].description)).toBeTruthy(); + }); + }); + describe('alert suppression', () => { const ruleTypesWithoutSuppression: Type[] = ['eql', 'esql', 'machine_learning', 'new_terms']; const suppressionFields = { diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/description_step/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/description_step/index.tsx index f6f5957b54e42..64cf8b7e1e83b 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/description_step/index.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/description_step/index.tsx @@ -47,6 +47,7 @@ import { buildAlertSuppressionWindowDescription, buildAlertSuppressionMissingFieldsDescription, buildHighlightedFieldsOverrideDescription, + buildSetupDescription, } from './helpers'; import * as i18n from './translations'; import { buildMlJobsDescription } from './build_ml_jobs_description'; @@ -301,6 +302,9 @@ export const getDescriptionItem = ( } else if (field === 'note') { const val: string = get(field, data); return buildNoteDescription(label, val); + } else if (field === 'setup') { + const val: string = get(field, data); + return buildSetupDescription(label, val); } else if (field === 'ruleType') { const ruleType: Type = get(field, data); return buildRuleTypeDescription(label, ruleType); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.test.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.test.ts index 86bcebb72ded5..71fe20ba3e6fb 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.test.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.test.ts @@ -556,6 +556,7 @@ describe('helpers', () => { tags: ['tag1', 'tag2'], threat: getThreatMock(), investigation_fields: { field_names: ['foo', 'bar'] }, + setup: '# this is some setup documentation', }; expect(result).toEqual(expected); @@ -637,6 +638,7 @@ describe('helpers', () => { tags: ['tag1', 'tag2'], threat: getThreatMock(), investigation_fields: { field_names: ['foo', 'bar'] }, + setup: '# this is some setup documentation', }; expect(result).toEqual(expected); @@ -662,6 +664,7 @@ describe('helpers', () => { tags: ['tag1', 'tag2'], threat: getThreatMock(), investigation_fields: { field_names: ['foo', 'bar'] }, + setup: '# this is some setup documentation', }; expect(result).toEqual(expected); @@ -706,6 +709,7 @@ describe('helpers', () => { tags: ['tag1', 'tag2'], threat: getThreatMock(), investigation_fields: { field_names: ['foo', 'bar'] }, + setup: '# this is some setup documentation', }; expect(result).toEqual(expected); @@ -759,6 +763,7 @@ describe('helpers', () => { }, ], investigation_fields: { field_names: ['foo', 'bar'] }, + setup: '# this is some setup documentation', }; expect(result).toEqual(expected); @@ -788,6 +793,7 @@ describe('helpers', () => { timestamp_override: 'event.ingest', timestamp_override_fallback_disabled: true, investigation_fields: { field_names: ['foo', 'bar'] }, + setup: '# this is some setup documentation', }; expect(result).toEqual(expected); @@ -818,6 +824,7 @@ describe('helpers', () => { timestamp_override_fallback_disabled: undefined, threat: getThreatMock(), investigation_fields: undefined, + setup: '# this is some setup documentation', }; expect(result).toEqual(expected); @@ -847,6 +854,7 @@ describe('helpers', () => { threat_indicator_path: undefined, timestamp_override: undefined, timestamp_override_fallback_disabled: undefined, + setup: '# this is some setup documentation', }; expect(result).toEqual(expected); @@ -876,6 +884,7 @@ describe('helpers', () => { threat_indicator_path: undefined, timestamp_override: undefined, timestamp_override_fallback_disabled: undefined, + setup: '# this is some setup documentation', }; expect(result).toEqual(expected); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.ts index e0e311fe55550..924dc4a62fd70 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.ts @@ -557,7 +557,6 @@ export const formatAboutStepData = ( isAssociatedToEndpointList, isBuildingBlock, note, - setup, ruleNameOverride, threatIndicatorPath, timestampOverride, @@ -611,7 +610,6 @@ export const formatAboutStepData = ( timestamp_override: timestampOverride !== '' ? timestampOverride : undefined, timestamp_override_fallback_disabled: timestampOverrideFallbackDisabled, ...(!isEmpty(note) ? { note } : {}), - ...(!isEmpty(setup) ? { setup } : {}), ...rest, }; return resp; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/__mocks__/mock.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/__mocks__/mock.ts index 9530daafde534..49bd1649c3471 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/__mocks__/mock.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/__mocks__/mock.ts @@ -197,7 +197,7 @@ export const mockAboutStepRule = (): AboutStepRule => ({ tags: ['tag1', 'tag2'], threat: getThreatMock(), note: '# this is some markdown documentation', - setup: '# this is some setup markdown documentation', + setup: '# this is some setup documentation', investigationFields: ['foo', 'bar'], }); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/types.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/types.ts index d6cf3666bffff..fa0168c7d2e98 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/types.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/types.ts @@ -241,6 +241,7 @@ export interface AboutStepRuleJson { rule_name_override?: RuleNameOverride; tags: string[]; threat: Threats; + setup: string; threat_indicator_path?: string; timestamp_override?: TimestampOverride; timestamp_override_fallback_disabled?: boolean; From 2d8966476af4b9f4309e4d0ab5df56a17f3fe92c Mon Sep 17 00:00:00 2001 From: Davis Plumlee Date: Fri, 8 Mar 2024 11:11:01 -0500 Subject: [PATCH 5/6] addresses comments and adds ftr tests --- .../components/description_step/helpers.tsx | 9 +++- .../components/step_about_rule/schema.tsx | 2 +- .../logic/actions/duplicate_rule.test.ts | 16 ------- .../logic/actions/duplicate_rule.ts | 2 - .../normalization/rule_converters.ts | 3 -- .../create_rules.ts | 22 ++++++++++ .../patch_rules.ts | 32 ++++++++++++++ .../update_rules.ts | 43 +++++++++++++++++++ 8 files changed, 105 insertions(+), 24 deletions(-) diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/description_step/helpers.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/description_step/helpers.tsx index 8681386c1b103..e2cc66ae7990c 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/description_step/helpers.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/description_step/helpers.tsx @@ -57,6 +57,11 @@ const NoteDescriptionContainer = styled(EuiFlexItem)` overflow-y: hidden; `; +const SetupDescriptionContainer = styled(EuiFlexItem)` + height: 105px; + overflow-y: hidden; +`; + export const isNotEmptyArray = (values: string[]) => !isEmpty(values.join('')); const EuiBadgeWrap = styled(EuiBadge)` @@ -642,11 +647,11 @@ export const buildSetupDescription = (label: string, setup: string): ListItems[] { title: label, description: ( - +
{setup}
-
+ ), }, ]; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_about_rule/schema.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_about_rule/schema.tsx index 4875eae3d6bdf..710166de40319 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_about_rule/schema.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_about_rule/schema.tsx @@ -317,7 +317,7 @@ export const schema: FormSchema = { 'xpack.securitySolution.detectionEngine.createRule.stepAboutRule.setupHelpText', { defaultMessage: - 'This guide will appear on the rule details page and in timelines (as notes) created from detection alerts generated by this rule.', + 'Provide instructions on rule prerequisites such as required integrations, configuration steps, and anything else needed for the rule to work correctly.', } ), labelAppend: OptionalFieldLabel, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_rule.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_rule.test.ts index 7223b920c7bdc..c0cb5f903c3ea 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_rule.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_rule.test.ts @@ -224,22 +224,6 @@ describe('duplicateRule', () => { }) ); }); - - it('resets setup guide to an empty string', async () => { - const rule = createPrebuiltRule(); - rule.params.setup = `## Config\n\nThe 'Audit Detailed File Share' audit policy must be configured...`; - const result = await duplicateRule({ - rule, - }); - - expect(result).toEqual( - expect.objectContaining({ - params: expect.objectContaining({ - setup: '', - }), - }) - ); - }); }); describe('when duplicating a custom (mutable) rule', () => { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_rule.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_rule.ts index 315517504def4..57931dca00c1e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_rule.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_rule.ts @@ -33,7 +33,6 @@ export const duplicateRule = async ({ rule }: DuplicateRuleParams): Promise ): InternalRuleUpdate => { @@ -487,7 +485,6 @@ export const convertCreateAPIToInternalSchema = ( input: RuleCreateProps & { related_integrations?: RelatedIntegrationArray; required_fields?: RequiredFieldArray; - setup?: SetupGuide; }, immutable = false, defaultEnabled = true diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_creation/trial_license_complete_tier/create_rules.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_creation/trial_license_complete_tier/create_rules.ts index 91fcf8e4f1a94..6c581f18c0a36 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_creation/trial_license_complete_tier/create_rules.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_creation/trial_license_complete_tier/create_rules.ts @@ -778,5 +778,27 @@ export default ({ getService }: FtrProviderContext) => { }); }); }); + + describe('setup guide', async () => { + beforeEach(async () => { + await deleteAllAlerts(supertest, log, es); + await deleteAllRules(supertest, log); + }); + + it('creates a rule with a setup guide when setup parameter is present', async () => { + const { body } = await supertest + .post(DETECTION_ENGINE_RULES_URL) + .set('kbn-xsrf', 'true') + .set('elastic-api-version', '2023-10-31') + .send( + getCustomQueryRuleParams({ + setup: 'A setup guide', + }) + ) + .expect(200); + + expect(body.setup).toEqual('A setup guide'); + }); + }); }); }; diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_patch/trial_license_complete_tier/patch_rules.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_patch/trial_license_complete_tier/patch_rules.ts index edd84f6c86650..24919448b8522 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_patch/trial_license_complete_tier/patch_rules.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_patch/trial_license_complete_tier/patch_rules.ts @@ -656,5 +656,37 @@ export default ({ getService }: FtrProviderContext) => { }); }); }); + + describe('setup guide', () => { + beforeEach(async () => { + await createAlertsIndex(supertest, log); + }); + + afterEach(async () => { + await deleteAllAlerts(supertest, log, es); + await deleteAllRules(supertest, log); + }); + + it('should overwrite setup field on patch', async () => { + await createRule(supertest, log, { + ...getSimpleRule('rule-1'), + setup: 'A setup guide', + }); + + const rulePatch = { + rule_id: 'rule-1', + setup: 'A different setup guide', + }; + + const { body } = await supertest + .patch(DETECTION_ENGINE_RULES_URL) + .set('kbn-xsrf', 'true') + .set('elastic-api-version', '2023-10-31') + .send(rulePatch) + .expect(200); + + expect(body.setup).to.eql('A different setup guide'); + }); + }); }); }; diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_update/trial_license_complete_tier/update_rules.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_update/trial_license_complete_tier/update_rules.ts index b341be9379914..5f1cd961c04eb 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_update/trial_license_complete_tier/update_rules.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_update/trial_license_complete_tier/update_rules.ts @@ -864,6 +864,49 @@ export default ({ getService }: FtrProviderContext) => { expect(body.investigation_fields).to.eql(undefined); }); }); + + describe('setup guide', () => { + it('should overwrite setup value on update', async () => { + await createRule(supertest, log, { + ...getSimpleRule('rule-1'), + setup: 'A setup guide', + }); + + const ruleUpdate = { + ...getSimpleRuleUpdate('rule-1'), + setup: 'A different setup guide', + }; + + const { body } = await supertest + .put(DETECTION_ENGINE_RULES_URL) + .set('kbn-xsrf', 'true') + .set('elastic-api-version', '2023-10-31') + .send(ruleUpdate) + .expect(200); + + expect(body.setup).to.eql('A different setup guide'); + }); + + it('should reset setup field to empty string on unset', async () => { + await createRule(supertest, log, { + ...getSimpleRule('rule-1'), + setup: 'A setup guide', + }); + + const ruleUpdate = { + ...getSimpleRuleUpdate('rule-1'), + setup: undefined, + }; + + const { body } = await supertest + .put(DETECTION_ENGINE_RULES_URL) + .set('kbn-xsrf', 'true') + .send(ruleUpdate) + .expect(200); + + expect(body.setup).to.eql(''); + }); + }); }); }); }; From 7266ec8bd186243ad6b5936a56388ecac685974a Mon Sep 17 00:00:00 2001 From: Davis Plumlee Date: Mon, 1 Apr 2024 18:40:22 -0400 Subject: [PATCH 6/6] updates tests --- .../trial_license_complete_tier/update_rules.ts | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_update/trial_license_complete_tier/update_rules.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_update/trial_license_complete_tier/update_rules.ts index afe40963adcab..500eedb5bc2fd 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_update/trial_license_complete_tier/update_rules.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_update/trial_license_complete_tier/update_rules.ts @@ -770,12 +770,7 @@ export default ({ getService }: FtrProviderContext) => { setup: 'A different setup guide', }; - const { body } = await supertest - .put(DETECTION_ENGINE_RULES_URL) - .set('kbn-xsrf', 'true') - .set('elastic-api-version', '2023-10-31') - .send(ruleUpdate) - .expect(200); + const { body } = await securitySolutionApi.updateRule({ body: ruleUpdate }).expect(200); expect(body.setup).to.eql('A different setup guide'); }); @@ -791,11 +786,7 @@ export default ({ getService }: FtrProviderContext) => { setup: undefined, }; - const { body } = await supertest - .put(DETECTION_ENGINE_RULES_URL) - .set('kbn-xsrf', 'true') - .send(ruleUpdate) - .expect(200); + const { body } = await securitySolutionApi.updateRule({ body: ruleUpdate }).expect(200); expect(body.setup).to.eql(''); });