-
Notifications
You must be signed in to change notification settings - Fork 8.6k
[Security Solution] Discover Security Profile Changes + Event timeline redirection #204756
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
Merged
Merged
Changes from all commits
Commits
Show all changes
55 commits
Select commit
Hold shift + click to select a range
5438e0b
incremental
logeekal 20e2776
incremental save
logeekal cb34563
poc: Overview tab
logeekal 986be47
feat: timeline redirect basic logic
logeekal 6b16fda
feat: default columns + row indicator
logeekal 7364df3
[CI] Auto-commit changed files from 'node scripts/notice'
kibanamachine 137c4a4
make ci 🟢
logeekal 68bb477
fix: types
logeekal 19e2eb3
tests: add
logeekal 0584947
test: unfocus
logeekal 665b15a
fix: timeline redirect url + tests
logeekal f100b30
fix: timerange sync issue
logeekal 79c2490
fix: housekeeping
logeekal 0073e2f
fix: rebase issues
logeekal aef1bd4
fix: more rebase issues
logeekal 841e7b9
fix: remove unnecessary files
logeekal 12561c9
fix: tests
logeekal 94fd20d
feat: add row action
logeekal fa73ec2
fix: additional files
logeekal 7d9a639
fix: types + remove unnecessary files
logeekal 61edf6a
tests
logeekal 6423ec3
more tests housekeeping
logeekal 7cb5f17
test: ftr context awareness
logeekal 145b2b5
translations for label
logeekal c875217
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine d580cc7
fix: add link button
logeekal 81f9617
[CI] Auto-commit changed files from 'node scripts/yarn_deduplicate'
kibanamachine 0142cf5
Merge branch 'main' into feat/timeline_redirect
logeekal aac8a4f
update codeowners
logeekal 035df6e
fix: jest tests
logeekal 48a3521
fix: FTR test
logeekal 6f52ae9
fix: cypress
logeekal b2b9795
Merge branch 'main' into feat/timeline_redirect
logeekal 9741ed1
fix: remove row action + add flyout overview tab
logeekal 25c0a16
Merge main --> current branch
logeekal 8293ab0
fix: tests
logeekal a5221dc
fix: readable url generations
logeekal bd76727
fix: remove dead code
logeekal adaedf7
fix: comment
logeekal 34e73da
fix: import
logeekal 106298d
fix: types
logeekal 376295c
fix: test
logeekal 5dd1c79
fix: cypress
logeekal 20ba5b6
Merge branch 'main' into feat/timeline_redirect
logeekal 2d36491
Merge branch 'main' into feat/timeline_redirect
logeekal 31a7702
fix: translations
logeekal 582aaca
fix: default app state
logeekal 2877773
fix: conditional profile changes
logeekal 115599c
Merge branch 'main' into feat/timeline_redirect
logeekal fade055
fix: PR Feedback
logeekal 1ad7ae1
fix: translations
logeekal 619e284
remove extra enum key
logeekal 8b8ba69
Fix failing tests after enabling security profile
davismcphee e5507d4
Update stateful tests
davismcphee e2874dc
Merge pull request #5 from davismcphee/fix-security-profile-tests
logeekal File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
60 changes: 60 additions & 0 deletions
60
...blic/context_awareness/profile_providers/security/accessors/get_default_app_state.test.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| /* | ||
| * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side | ||
| * Public License v 1"; you may not use this file except in compliance with, at | ||
| * your election, the "Elastic License 2.0", the "GNU Affero General Public | ||
| * License v3.0 only", or the "Server Side Public License, v 1". | ||
| */ | ||
|
|
||
| import { createDefaultSecuritySolutionAppStateGetter } from './get_default_app_state'; | ||
|
|
||
| describe('createDefaultSecuritySolutionAppStateGetter', () => { | ||
| it('should return default app state without security solution specific columns and breakdown field if there is no index match', () => { | ||
| const getDefaultAppState = createDefaultSecuritySolutionAppStateGetter(); | ||
|
|
||
| const params = { | ||
| dataView: { | ||
| getIndexPattern: () => 'logs-*', | ||
| }, | ||
| }; | ||
|
|
||
| const prevAppState = { someKey: 'someValue' }; | ||
| const prevAppStateGetter = () => prevAppState; | ||
| // @ts-expect-error - params should be compatible with the expected type | ||
| const appState = getDefaultAppState(prevAppStateGetter)(params); | ||
|
|
||
| expect(Object.keys(appState)).toMatchObject(['someKey']); | ||
| }); | ||
|
|
||
| it('should return default app state with security solution specific columns and breakdown field if there is index match', () => { | ||
| const getDefaultAppState = createDefaultSecuritySolutionAppStateGetter(); | ||
|
|
||
| const params = { | ||
| dataView: { | ||
| getIndexPattern: () => '.alerts-security.alerts-*', | ||
| }, | ||
| }; | ||
|
|
||
| const prevAppState = { someKey: 'someValue' }; | ||
| const prevAppStateGetter = () => prevAppState; | ||
| // @ts-expect-error - params should be compatible with the expected type | ||
| const appState = getDefaultAppState(prevAppStateGetter)(params); | ||
|
|
||
| expect(appState).toEqual({ | ||
| ...prevAppState, | ||
| breakdownField: 'kibana.alert.workflow_status', | ||
| columns: [ | ||
| { name: '@timestamp', width: 218 }, | ||
| { name: 'kibana.alert.workflow_status' }, | ||
| { name: 'message', width: 360 }, | ||
| { name: 'event.category' }, | ||
| { name: 'event.action' }, | ||
| { name: 'host.name' }, | ||
| { name: 'source.ip' }, | ||
| { name: 'destination.ip' }, | ||
| { name: 'user.name' }, | ||
| ], | ||
| }); | ||
| }); | ||
| }); |
55 changes: 55 additions & 0 deletions
55
...er/public/context_awareness/profile_providers/security/accessors/get_default_app_state.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| /* | ||
| * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side | ||
| * Public License v 1"; you may not use this file except in compliance with, at | ||
| * your election, the "Elastic License 2.0", the "GNU Affero General Public | ||
| * License v3.0 only", or the "Server Side Public License, v 1". | ||
| */ | ||
|
|
||
| import type { RootProfileProvider } from '../../../profiles'; | ||
| import { ALERTS_INDEX_PATTERN } from '../constants'; | ||
|
|
||
| export const createDefaultSecuritySolutionAppStateGetter: () => RootProfileProvider['profile']['getDefaultAppState'] = | ||
| () => (prev) => (params) => { | ||
| const { dataView } = params; | ||
| const appState = { ...prev(params) }; | ||
| if (!dataView.getIndexPattern().includes(ALERTS_INDEX_PATTERN)) { | ||
| return appState; | ||
| } | ||
| return { | ||
| ...appState, | ||
| breakdownField: 'kibana.alert.workflow_status', | ||
| columns: [ | ||
| { | ||
| name: '@timestamp', | ||
| width: 218, | ||
| }, | ||
| { | ||
| name: 'kibana.alert.workflow_status', | ||
| }, | ||
| { | ||
| name: 'message', | ||
| width: 360, | ||
| }, | ||
| { | ||
| name: 'event.category', | ||
| }, | ||
| { | ||
| name: 'event.action', | ||
| }, | ||
| { | ||
| name: 'host.name', | ||
| }, | ||
| { | ||
| name: 'source.ip', | ||
| }, | ||
| { | ||
| name: 'destination.ip', | ||
| }, | ||
| { | ||
| name: 'user.name', | ||
| }, | ||
| ], | ||
| }; | ||
| }; | ||
57 changes: 57 additions & 0 deletions
57
...r/public/context_awareness/profile_providers/security/accessors/get_row_indicator.test.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| /* | ||
| * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side | ||
| * Public License v 1"; you may not use this file except in compliance with, at | ||
| * your election, the "Elastic License 2.0", the "GNU Affero General Public | ||
| * License v3.0 only", or the "Server Side Public License, v 1". | ||
| */ | ||
|
|
||
| import type { DataTableRecord } from '@kbn/discover-utils'; | ||
| import { getAlertEventRowIndicator } from './get_row_indicator'; | ||
| import type { EuiThemeComputed } from '@elastic/eui'; | ||
|
|
||
| describe('getAlertEventRowIndicator', () => { | ||
| it('should return the correct color and label for an event row', () => { | ||
| const row = { | ||
| flattened: { | ||
| 'event.kind': 'event', | ||
| }, | ||
| } as unknown as DataTableRecord; | ||
|
|
||
| const euiTheme = { | ||
| colors: { | ||
| backgroundLightText: 'backgroundLightText', | ||
| }, | ||
| } as const as EuiThemeComputed; | ||
|
|
||
| const result = getAlertEventRowIndicator(row, euiTheme); | ||
|
|
||
| expect(result).toEqual({ | ||
| color: 'backgroundLightText', | ||
| label: 'event', | ||
| }); | ||
| }); | ||
|
|
||
| it('should return the correct color and label for an alert row', () => { | ||
| const row = { | ||
| flattened: { | ||
| 'event.kind': 'signal', | ||
| }, | ||
| } as unknown as DataTableRecord; | ||
|
|
||
| const euiTheme = { | ||
| colors: { | ||
| backgroundLightText: 'backgroundLightText', | ||
| warning: 'warning', | ||
| }, | ||
| } as const as EuiThemeComputed; | ||
|
|
||
| const result = getAlertEventRowIndicator(row, euiTheme); | ||
|
|
||
| expect(result).toEqual({ | ||
| color: 'warning', | ||
| label: 'alert', | ||
| }); | ||
| }); | ||
| }); |
29 changes: 29 additions & 0 deletions
29
...scover/public/context_awareness/profile_providers/security/accessors/get_row_indicator.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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", the "GNU Affero General Public License v3.0 only", and the "Server Side | ||
| * Public License v 1"; you may not use this file except in compliance with, at | ||
| * your election, the "Elastic License 2.0", the "GNU Affero General Public | ||
| * License v3.0 only", or the "Server Side Public License, v 1". | ||
| */ | ||
|
|
||
| import { getFieldValue } from '@kbn/discover-utils'; | ||
| import type { UnifiedDataTableProps } from '@kbn/unified-data-table'; | ||
|
|
||
| export const getAlertEventRowIndicator: NonNullable<UnifiedDataTableProps['getRowIndicator']> = ( | ||
|
PhilippeOberti marked this conversation as resolved.
|
||
| row, | ||
| euiTheme | ||
| ) => { | ||
| let eventColor = euiTheme.colors.backgroundLightText; | ||
| let rowLabel = 'event'; | ||
|
|
||
| if (getFieldValue(row, 'event.kind') === 'signal') { | ||
| eventColor = euiTheme.colors.warning; | ||
| rowLabel = 'alert'; | ||
| } | ||
|
|
||
| return { | ||
| color: eventColor, | ||
| label: rowLabel, | ||
| }; | ||
| }; | ||
146 changes: 146 additions & 0 deletions
146
...lic/context_awareness/profile_providers/security/components/alert_event_overview.test.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,146 @@ | ||
| /* | ||
| * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side | ||
| * Public License v 1"; you may not use this file except in compliance with, at | ||
| * your election, the "Elastic License 2.0", the "GNU Affero General Public | ||
| * License v3.0 only", or the "Server Side Public License, v 1". | ||
| */ | ||
|
|
||
| import React from 'react'; | ||
| import { fireEvent, render, screen } from '@testing-library/react'; | ||
| import { AlertEventOverview } from './alert_event_overview'; | ||
| import type { DataTableRecord } from '@kbn/discover-utils'; | ||
| import { dataViewMock } from '@kbn/discover-utils/src/__mocks__'; | ||
| import { EcsFlat } from '@elastic/ecs'; | ||
| import { useDiscoverServices } from '../../../../hooks/use_discover_services'; | ||
| import { encode } from '@kbn/rison'; | ||
| import { URLSearchParams } from 'url'; | ||
|
|
||
| jest.mock('../../../../hooks/use_discover_services'); | ||
|
|
||
| const TEST_TIMELINE_URL = 'test-timeline-url'; | ||
|
|
||
| const mockGetUrlForApp = jest.fn().mockReturnValue(TEST_TIMELINE_URL); | ||
|
|
||
| const mockDiscoverServices = { | ||
| application: { | ||
| getUrlForApp: mockGetUrlForApp, | ||
| }, | ||
| }; | ||
|
|
||
| const mockRow = { | ||
| 'kibana.alert.reason': 'test-reason', | ||
| 'kibana.alert.rule.description': 'test-description', | ||
| 'event.kind': 'signal', | ||
| _id: 'test-id', | ||
| '@timestamp': '2021-08-02T14:00:00.000Z', | ||
| 'kibana.alert.url': 'test-url', | ||
| }; | ||
|
|
||
| const mockHit = { | ||
| flattened: mockRow, | ||
| } as unknown as DataTableRecord; | ||
|
|
||
| const mockDataView = dataViewMock; | ||
|
|
||
| describe('AlertEventOverview', () => { | ||
| beforeEach(() => { | ||
| (useDiscoverServices as jest.Mock).mockReturnValue(mockDiscoverServices); | ||
| }); | ||
| describe('expandable sections', () => { | ||
| test('should return the expandable sections correctly', () => { | ||
| render(<AlertEventOverview hit={mockHit} dataView={mockDataView} />); | ||
| expect(screen.getByTestId('expandableHeader-About')).toBeVisible(); | ||
| expect(screen.getByTestId('expandableContent-About')).toBeVisible(); | ||
|
|
||
| fireEvent.click(screen.getByTestId('expandableHeader-About')); | ||
| expect(screen.getByTestId('expandableContent-About')).not.toBeVisible(); | ||
| }); | ||
|
|
||
| test('should show expected sections', () => { | ||
| render(<AlertEventOverview hit={mockHit} dataView={mockDataView} />); | ||
| expect(screen.getByTestId('expandableHeader-About')).toBeVisible(); | ||
|
|
||
| expect(screen.getByTestId('expandableHeader-Description')).toBeVisible(); | ||
| expect(screen.getByTestId('expandableContent-Description')).toHaveTextContent( | ||
| 'test-description' | ||
| ); | ||
|
|
||
| expect(screen.getByTestId('expandableHeader-Reason')).toBeVisible(); | ||
| expect(screen.getByTestId('expandableContent-Reason')).toHaveTextContent('test-reason'); | ||
|
|
||
| expect(screen.getByTestId('exploreSecurity')).toBeVisible(); | ||
|
|
||
| expect(screen.getByTestId('exploreSecurity').getAttribute('href')).toBe('test-url'); | ||
| }); | ||
| }); | ||
|
|
||
| describe('data', () => { | ||
| test('should return Ecs description for different event types correctly', () => { | ||
| const localMockHit = { | ||
| flattened: { | ||
| ...mockRow, | ||
| 'event.category': 'process', | ||
| }, | ||
| } as unknown as DataTableRecord; | ||
|
|
||
| render(<AlertEventOverview hit={localMockHit} dataView={mockDataView} />); | ||
|
|
||
| expect(screen.getByTestId('expandableContent-About')).toHaveTextContent( | ||
| EcsFlat['event.category'].allowed_values.find((i) => i.name === 'process') | ||
| ?.description as string | ||
| ); | ||
| }); | ||
|
|
||
| test('should display timeline redirect url correctly', () => { | ||
| const localMockHit = { | ||
| flattened: { | ||
| ...mockRow, | ||
| 'event.kind': 'event', | ||
| 'event.category': 'process', | ||
| }, | ||
| } as unknown as DataTableRecord; | ||
| render(<AlertEventOverview hit={localMockHit} dataView={mockDataView} />); | ||
| const expectedURLJSON = { | ||
| timeline: encode({ | ||
| activeTab: 'query', | ||
| isOpen: true, | ||
| query: { | ||
| expression: '_id: test-id', | ||
| kind: 'kuery', | ||
| }, | ||
| }), | ||
|
|
||
| timeRange: encode({ | ||
| timeline: { | ||
| timerange: { | ||
| from: mockRow['@timestamp'], | ||
| to: mockRow['@timestamp'], | ||
| kind: 'absolute', | ||
| linkTo: false, | ||
| }, | ||
| }, | ||
| }), | ||
|
|
||
| timelineFlyout: encode({ | ||
| right: { | ||
| id: 'document-details-right', | ||
| params: { | ||
| id: 'test-id', | ||
| scopeId: 'timeline-1', | ||
| }, | ||
| }, | ||
| }), | ||
| }; | ||
|
|
||
| const searchParams = new URLSearchParams( | ||
| `timeline=${expectedURLJSON.timeline}&timerange=${expectedURLJSON.timeRange}&timelineFlyout=${expectedURLJSON.timelineFlyout}` | ||
| ); | ||
|
|
||
| expect(screen.getByTestId('exploreSecurity').getAttribute('href')).toBe( | ||
| `test-timeline-url?${searchParams}` | ||
| ); | ||
| }); | ||
| }); | ||
| }); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would be best modelled as a dedicated data source profile that resolves based on solution + index pattern, rather than repeating the logic in each extension point, e.g.:
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks I was trying to see how can i get index pattern in context. This is great. Let me see i can make a change now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay I tried to implement this Datasource Profile Provider. In
SecuritySolution View in ECH,params.rootContext.solutionTypeis notSolutionType.SecuritybutSolutionType.defaultas can be seen in screenshot below. Because of this profile is not get activated.For now, i will keep it as it is and raise a follow up PR for the same.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No worries about addressing for now, it makes sense to look into as a followup. That's very odd though 🤔 To clarify, you'd still need a
SecurityRootProfileto setrootContext.solutionTypetoSolutionType.Security, but the data source specific pieces could be extracted into a separateSecurityAlertsDataSourceProfileProvideror similar that resolves based onrootContext.solutionType+ the index pattern, instead of including that logic in each root profile extension point implementation.