Skip to content

Commit

Permalink
[7.x] [Security Solution][Exceptions] - Moves remaining exceptions bu…
Browse files Browse the repository at this point in the history
…ilder logic into lists plugin (#95266) (#96358)

* [Security Solution][Exceptions] - Moves remaining exceptions builder logic into lists plugin (#95266)

## Summary

Moves part of the exceptions UI out of the security solution plugin and into the lists plugin. In order to keep PRs (relatively) small, I am moving single components at a time. This should also then help more easily pinpoint the source of any issues that come up along the way.

The next couple PRs will focus on the exception builder. This one in particular is focused on moving over the `ExceptionBuilderComponent` which deals with rendering numerous exception items and their entries.

Quick Summary:
- `x-pack/plugins/security_solution/public/common/components/exceptions/builder/` → ` x-pack/plugins/lists/public/exceptions/components/builder/`
  - Corresponding unit test file moved as well
  - Updated security solution exception builder to pull `ExceptionBuilderComponent` from lists plugin
# Conflicts:
#	packages/kbn-optimizer/limits.yml
#	src/dev/code_coverage/ingest_coverage/__tests__/enumerate_patterns.test.js

* removing file that exists in master but not backported to 7.x mistakenly included in backport upon cherry picking

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
  • Loading branch information
yctercero and kibanamachine committed Apr 11, 2021
1 parent 17fafef commit 8d534f4
Show file tree
Hide file tree
Showing 27 changed files with 817 additions and 1,022 deletions.
4 changes: 2 additions & 2 deletions packages/kbn-optimizer/limits.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ pageLoadAssetSize:
lens: 96624
licenseManagement: 41817
licensing: 29004
lists: 202261
lists: 228500
logstash: 53548
management: 46112
maps: 80000
Expand All @@ -68,7 +68,7 @@ pageLoadAssetSize:
searchprofiler: 67080
security: 189428
securityOss: 30806
securitySolution: 283440
securitySolution: 235402
share: 99205
snapshotRestore: 79176
spaces: 389643
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,358 @@
/*
* 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.
*/
/*
* 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 { Story, addDecorator } from '@storybook/react';
import React from 'react';
import { ThemeProvider } from 'styled-components';
import euiLightVars from '@elastic/eui/dist/eui_theme_light.json';
import { HttpStart } from 'kibana/public';

import { AutocompleteStart } from '../../../../../../../src/plugins/data/public';
import {
fields,
getField,
} from '../../../../../../../src/plugins/data/common/index_patterns/fields/fields.mocks';
import { getMockTheme } from '../../../common/test_utils/kibana_react.mock';
import { getEntryMatchAnyMock } from '../../../../common/schemas/types/entry_match_any.mock';
import { getEntryMatchMock } from '../../../../common/schemas/types/entry_match.mock';
import { getEntryExistsMock } from '../../../../common/schemas/types/entry_exists.mock';
import { getEntryNestedMock } from '../../../../common/schemas/types/entry_nested.mock';
import { getExceptionListItemSchemaMock } from '../../../../common/schemas/response/exception_list_item_schema.mock';

import {
ExceptionBuilderComponent,
ExceptionBuilderProps,
OnChangeProps,
} from './exception_items_renderer';

const mockTheme = getMockTheme({
darkMode: false,
eui: euiLightVars,
});
const mockHttpService: HttpStart = ({
addLoadingCountSource: (): void => {},
anonymousPaths: {
isAnonymous: (): void => {},
register: (): void => {},
},
basePath: {},
delete: (): void => {},
externalUrl: {
validateUrl: (): void => {},
},
fetch: (): void => {},
get: (): void => {},
getLoadingCount$: (): void => {},
head: (): void => {},
intercept: (): void => {},
options: (): void => {},
patch: (): void => {},
post: (): void => {},
put: (): void => {},
} as unknown) as HttpStart;
const mockAutocompleteService = ({
getValueSuggestions: () =>
new Promise((resolve) => {
setTimeout(() => {
resolve([
'siem-kibana',
'win2019-endpoint-mr-pedro',
'rock01',
'windows-endpoint',
'siem-windows',
'mothra',
]);
}, 300);
}),
} as unknown) as AutocompleteStart;

addDecorator((storyFn) => <ThemeProvider theme={mockTheme}>{storyFn()}</ThemeProvider>);

export default {
argTypes: {
allowLargeValueLists: {
control: {
type: 'boolean',
},
description: '`boolean` - set to true to allow large value lists.',
table: {
defaultValue: {
summary: true,
},
},
type: {
required: true,
},
},
autocompleteService: {
control: {
type: 'object',
},
description:
'`AutocompleteStart` - Kibana data plugin autocomplete service used for field value autocomplete.',
type: {
required: true,
},
},
exceptionListItems: {
control: {
type: 'array',
},
description:
'`ExceptionsBuilderExceptionItem[]` - Any existing exception items - would be populated when editing an exception item.',
type: {
required: true,
},
},
httpService: {
control: {
type: 'object',
},
description: '`HttpStart` - Kibana service.',
type: {
required: true,
},
},
indexPatterns: {
description:
'`IIndexPattern` - index patterns used to populate field options and value autocomplete.',
type: {
required: true,
},
},
isAndDisabled: {
control: {
type: 'boolean',
},
description:
'`boolean` - set to true to disallow users from using the `AND` button to add entries.',
table: {
defaultValue: {
summary: false,
},
},
type: {
required: true,
},
},
isNestedDisabled: {
control: {
type: 'boolean',
},
description:
'`boolean` - set to true to disallow users from using the `Add nested` button to add nested entries.',
table: {
defaultValue: {
summary: false,
},
},
type: {
required: true,
},
},
isOrDisabled: {
control: {
type: 'boolean',
},
description:
'`boolean` - set to true to disallow users from using the `OR` button to add multiple exception items within the builder.',
table: {
defaultValue: {
summary: false,
},
},
type: {
required: true,
},
},
listId: {
control: {
type: 'string',
},
description: '`string` - the exception list id.',
type: {
required: true,
},
},
listNamespaceType: {
control: {
options: ['agnostic', 'single'],
type: 'select',
},
description: '`NamespaceType` - Determines whether the list is global or space specific.',
type: {
required: true,
},
},
listType: {
control: {
options: ['detection', 'endpoint'],
type: 'select',
},
description:
'`ExceptionListType` - Depending on the list type, certain validations may apply.',
type: {
required: true,
},
},
listTypeSpecificIndexPatternFilter: {
description:
'`(pattern: IIndexPattern, type: ExceptionListType) => IIndexPattern` - callback invoked when index patterns filtered. Optional to be used if you would only like certain fields displayed.',
type: {
required: false,
},
},
onChange: {
description:
'`(arg: OnChangeProps) => void` - callback invoked any time builder update to propagate changes up to parent.',
type: {
required: true,
},
},
ruleName: {
description: '`string` - name of the rule list is associated with.',
type: {
required: true,
},
},
},
component: ExceptionBuilderComponent,
title: 'ExceptionBuilderComponent',
};

const BuilderTemplate: Story<ExceptionBuilderProps> = (args) => (
<ExceptionBuilderComponent {...args} />
);

export const Default = BuilderTemplate.bind({});
Default.args = {
allowLargeValueLists: true,
autocompleteService: mockAutocompleteService,
exceptionListItems: [],
httpService: mockHttpService,
indexPatterns: { fields, id: '1234', title: 'logstash-*' },
isAndDisabled: false,
isNestedDisabled: false,
isOrDisabled: false,
listId: '1234',
listNamespaceType: 'single',
listType: 'detection',
onChange: (): OnChangeProps => ({
errorExists: false,
exceptionItems: [],
exceptionsToDelete: [],
}),
ruleName: 'My awesome rule',
};

const sampleExceptionItem = {
...getExceptionListItemSchemaMock(),
entries: [
{ ...getEntryMatchAnyMock(), field: getField('ip').name, value: ['some ip'] },
{ ...getEntryMatchMock(), field: getField('ssl').name, value: 'false' },
{ ...getEntryExistsMock(), field: getField('@timestamp').name },
],
};

const sampleNestedExceptionItem = {
...getExceptionListItemSchemaMock(),
entries: [
{ ...getEntryMatchAnyMock(), field: getField('ip').name, value: ['some ip'] },
{
...getEntryNestedMock(),
entries: [
{
...getEntryMatchMock(),
field: 'child',
value: 'some nested value',
},
],
field: 'nestedField',
},
],
};

const BuilderSingleExceptionItem: Story<ExceptionBuilderProps> = (args) => (
<ExceptionBuilderComponent {...args} />
);

export const SingleExceptionItem = BuilderSingleExceptionItem.bind({});
SingleExceptionItem.args = {
allowLargeValueLists: true,
autocompleteService: mockAutocompleteService,
exceptionListItems: [sampleExceptionItem],
httpService: mockHttpService,
indexPatterns: { fields, id: '1234', title: 'logstash-*' },
isAndDisabled: false,
isNestedDisabled: false,
isOrDisabled: false,
listId: '1234',
listNamespaceType: 'single',
listType: 'detection',
onChange: (): OnChangeProps => ({
errorExists: false,
exceptionItems: [sampleExceptionItem],
exceptionsToDelete: [],
}),
ruleName: 'My awesome rule',
};

const BuilderMultiExceptionItems: Story<ExceptionBuilderProps> = (args) => (
<ExceptionBuilderComponent {...args} />
);

export const MultiExceptionItems = BuilderMultiExceptionItems.bind({});
MultiExceptionItems.args = {
allowLargeValueLists: true,
autocompleteService: mockAutocompleteService,
exceptionListItems: [sampleExceptionItem, sampleExceptionItem],
httpService: mockHttpService,
indexPatterns: { fields, id: '1234', title: 'logstash-*' },
isAndDisabled: false,
isNestedDisabled: false,
isOrDisabled: false,
listId: '1234',
listNamespaceType: 'single',
listType: 'detection',
onChange: (): OnChangeProps => ({
errorExists: false,
exceptionItems: [sampleExceptionItem, sampleExceptionItem],
exceptionsToDelete: [],
}),
ruleName: 'My awesome rule',
};

const BuilderWithNested: Story<ExceptionBuilderProps> = (args) => (
<ExceptionBuilderComponent {...args} />
);

export const WithNestedExceptionItem = BuilderWithNested.bind({});
WithNestedExceptionItem.args = {
allowLargeValueLists: true,
autocompleteService: mockAutocompleteService,
exceptionListItems: [sampleNestedExceptionItem, sampleExceptionItem],
httpService: mockHttpService,
indexPatterns: { fields, id: '1234', title: 'logstash-*' },
isAndDisabled: false,
isNestedDisabled: false,
isOrDisabled: false,
listId: '1234',
listNamespaceType: 'single',
listType: 'detection',
onChange: (): OnChangeProps => ({
errorExists: false,
exceptionItems: [sampleNestedExceptionItem, sampleExceptionItem],
exceptionsToDelete: [],
}),
ruleName: 'My awesome rule',
};
Loading

0 comments on commit 8d534f4

Please sign in to comment.