Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions .buildkite/ftr_security_serverless_configs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ disabled:
- x-pack/test_serverless/functional/test_suites/security/config.cloud_security_posture.agentless_api.ts
- x-pack/test_serverless/functional/test_suites/security/config.saved_objects_management.ts
- x-pack/test_serverless/functional/test_suites/security/config.context_awareness.ts
- x-pack/test_serverless/functional/test_suites/security/config.examples.context_awareness.ts
- x-pack/test_serverless/functional/test_suites/security/common_configs/config.group1.ts
- x-pack/test_serverless/functional/test_suites/security/common_configs/config.group2.ts
- x-pack/test_serverless/functional/test_suites/security/common_configs/config.group3.ts
Expand Down
13 changes: 8 additions & 5 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -2296,6 +2296,9 @@ x-pack/solutions/security/plugins/security_solution/public/asset_inventory @elas
/x-pack/solutions/security/plugins/security_solution/public/siem_migrations @elastic/security-threat-hunting
/x-pack/solutions/security/plugins/security_solution/public/common/components/control_columns @elastic/security-threat-hunting

/x-pack/test_serverless/functional/test_suites/security/ftr/discover @elastic/security-threat-hunting
x-pack/test_serverless/functional/test_suites/security/config.context_awareness.ts @elastic/security-threat-hunting

## Security Solution Threat Hunting areas - Threat Hunting Investigations

/x-pack/solutions/security/plugins/security_solution/common/api/timeline @elastic/security-threat-hunting-investigations
Expand Down Expand Up @@ -2735,11 +2738,11 @@ packages/react @elastic/appex-sharedux
/x-pack/test_serverless/functional/test_suites/security/ftr/navigation.ts @elastic/appex-sharedux

# OpenAPI spec files
oas_docs/linters @elastic/core-docs @elastic/experience-docs
oas_docs/overlays @elastic/core-docs @elastic/experience-docs
oas_docs/kibana.info.serverless.yaml @elastic/core-docs @elastic/experience-docs
oas_docs/kibana.info.yaml @elastic/core-docs @elastic/experience-docs
oas_docs/output @elastic/core-docs @elastic/experience-docs
oas_docs/linters @elastic/core-docs @elastic/experience-docs
oas_docs/overlays @elastic/core-docs @elastic/experience-docs
oas_docs/kibana.info.serverless.yaml @elastic/core-docs @elastic/experience-docs
oas_docs/kibana.info.yaml @elastic/core-docs @elastic/experience-docs
oas_docs/output @elastic/core-docs @elastic/experience-docs

# Documentation settings files
docs/settings-gen @elastic/platform-docs @elastic/experience-docs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import type { ReactElement } from 'react';
import type { FunctionComponent } from 'react';
import type { EuiDataGridCellValueElementProps, EuiDataGridColumn } from '@elastic/eui';
import type { DataTableRecord } from '@kbn/discover-utils/src/types';
import type { DataView } from '@kbn/data-views-plugin/common';
Expand Down Expand Up @@ -46,10 +46,7 @@ export type DataGridCellValueElementProps = EuiDataGridCellValueElementProps & {
isCompressed?: boolean;
};

export type CustomCellRenderer = Record<
string,
(props: DataGridCellValueElementProps) => ReactElement
>;
export type CustomCellRenderer = Record<string, FunctionComponent<DataGridCellValueElementProps>>;

export interface CustomGridColumnProps {
column: EuiDataGridColumn;
Expand Down
1 change: 1 addition & 0 deletions src/platform/plugins/shared/discover/kibana.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"unifiedSearch",
"unifiedHistogram",
"contentManagement",
"discoverShared"
],
"optionalPlugins": [
"dataVisualizer",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import { createElement } from 'react';
import { createContextAwarenessMocks } from '../context_awareness/__mocks__';
import { DiscoverEBTManager } from '../plugin_imports/discover_ebt_manager';
import { createUrlTrackerMock } from './url_tracker.mock';
import { discoverSharedPluginMock } from '@kbn/discover-shared-plugin/public/mocks';

export function createDiscoverServicesMock(): DiscoverServices {
const dataPlugin = dataPluginMock.createStartContract();
Expand Down Expand Up @@ -263,6 +264,7 @@ export function createDiscoverServicesMock(): DiscoverServices {
profilesManager: profilesManagerMock,
ebtManager: new DiscoverEBTManager(),
setHeaderActionMenu: jest.fn(),
discoverShared: discoverSharedPluginMock.createStartContract().features,
} as unknown as DiscoverServices;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { createTracesDataSourceProfileProvider } from './observability/traces_da
import { createDeprecationLogsDataSourceProfileProvider } from './common/deprecation_logs';
import { createClassicNavRootProfileProvider } from './common/classic_nav_root_profile';
import { createObservabilityDocumentProfileProviders } from './observability/observability_profile_providers';
import { createSecurityDocumentProfileProvider } from './security/security_document_profile';

/**
* Register profile providers for root, data source, and document contexts to the profile profile services
Expand Down Expand Up @@ -158,5 +159,6 @@ const createDataSourceProfileProviders = (providerServices: ProfileProviderServi
*/
const createDocumentProfileProviders = (providerServices: ProfileProviderServices) => [
createExampleDocumentProfileProvider(),
createSecurityDocumentProfileProvider(providerServices),
...createObservabilityDocumentProfileProviders(providerServices),
];
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 React from 'react';
import type { SecuritySolutionCellRendererFeature } from '@kbn/discover-shared-plugin/public';
import type { DataGridCellValueElementProps } from '@kbn/unified-data-table';
import { createCellRendererAccessor } from './get_cell_renderer_accessor';
import { render } from '@testing-library/react';

const cellRendererFeature: SecuritySolutionCellRendererFeature = {
id: 'security-solution-cell-renderer',
getRenderer: async () => (fieldName: string) => {
if (fieldName === 'host.name') {
return (props: DataGridCellValueElementProps) => {
return <div data-test-subj="cell-render-feature">{props.columnId}</div>;
};
}
},
};

const mockCellProps = {
columnId: 'host.name',
row: {
id: '1',
raw: {},
flattened: {},
},
} as DataGridCellValueElementProps;

describe('getCellRendererAccessort', () => {
it('should return a cell renderer', async () => {
const getCellRenderer = await createCellRendererAccessor(cellRendererFeature);
expect(getCellRenderer).toBeDefined();
const CellRenderer = getCellRenderer?.('host.name') as React.FC<DataGridCellValueElementProps>;
expect(CellRenderer).toBeDefined();
const { getByTestId } = render(<CellRenderer {...mockCellProps} />);
expect(getByTestId('cell-render-feature')).toBeVisible();
expect(getByTestId('cell-render-feature')).toHaveTextContent('host.name');
});

it('should return undefined if cellRendererFeature is not defined', async () => {
const getCellRenderer = await createCellRendererAccessor();
expect(getCellRenderer).toBeUndefined();
});

it('should return undefined if cellRendererGetter returns undefined', async () => {
const getCellRenderer = await createCellRendererAccessor(cellRendererFeature);
const cellRenderer = getCellRenderer?.('user.name');
expect(cellRenderer).toBeUndefined();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", 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 type { SecuritySolutionCellRendererFeature } from '@kbn/discover-shared-plugin/public';
import type { DataGridCellValueElementProps } from '@kbn/unified-data-table';

export const createCellRendererAccessor = async (
cellRendererFeature?: SecuritySolutionCellRendererFeature
) => {
if (!cellRendererFeature) return undefined;
const cellRendererGetter = await cellRendererFeature.getRenderer();
function getCellRenderer(fieldName: string) {
const CellRenderer = cellRendererGetter(fieldName);
if (!CellRenderer) return undefined;
return React.memo(function SecuritySolutionCellRenderer(props: DataGridCellValueElementProps) {
return <CellRenderer {...props} />;
});
}

return getCellRenderer;
};
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' },
],
});
});
});
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',
},
],
};
};
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',
});
});
});
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']> = (
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,
};
};
Loading