Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

[8.x] [Security Solution] [Attack discovery] Fixes alerts filtering issues (#211371) #211645

Merged
merged 1 commit into from
Feb 18, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react';
import useLocalStorage from 'react-use/lib/useLocalStorage';

import { AlertsSettings } from './alerts_settings';
import { useSpaceId } from '../../../../common/hooks/use_space_id';
import { Footer } from '../../settings_flyout/footer';
import { getIsTourEnabled } from './is_tour_enabled';
import * as i18n from './translations';
Expand All @@ -45,7 +44,6 @@ const SettingsModalComponent: React.FC<Props> = ({
localStorageAttackDiscoveryMaxAlerts,
setLocalStorageAttackDiscoveryMaxAlerts,
}) => {
const spaceId = useSpaceId() ?? 'default';
const modalTitleId = useGeneratedHtmlId();

const [maxAlerts, setMaxAlerts] = useState(
Expand All @@ -68,7 +66,7 @@ const SettingsModalComponent: React.FC<Props> = ({
}, [closeModal, maxAlerts, setLocalStorageAttackDiscoveryMaxAlerts]);

const [showSettingsTour, setShowSettingsTour] = useLocalStorage<boolean>(
`${DEFAULT_ASSISTANT_NAMESPACE}.${ATTACK_DISCOVERY_STORAGE_KEY}.${spaceId}.${SHOW_SETTINGS_TOUR_LOCAL_STORAGE_KEY}.v8.16`,
`${DEFAULT_ASSISTANT_NAMESPACE}.${ATTACK_DISCOVERY_STORAGE_KEY}.${SHOW_SETTINGS_TOUR_LOCAL_STORAGE_KEY}.v8.16`,
true
);
const onTourFinished = useCallback(() => setShowSettingsTour(() => false), [setShowSettingsTour]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ import useLocalStorage from 'react-use/lib/useLocalStorage';
import { SecurityPageName } from '../../../common/constants';
import { HeaderPage } from '../../common/components/header_page';
import { useInvalidFilterQuery } from '../../common/hooks/use_invalid_filter_query';
import { useSpaceId } from '../../common/hooks/use_space_id';
import { useKibana } from '../../common/lib/kibana';
import { convertToBuildEsQuery } from '../../common/lib/kuery';
import { SpyRoute } from '../../common/utils/route/spy_routes';
Expand All @@ -53,8 +52,6 @@ const AttackDiscoveryPageComponent: React.FC = () => {
services: { uiSettings },
} = useKibana();

const spaceId = useSpaceId() ?? 'default';

const {
assistantFeatures: { attackDiscoveryAlertFiltering },
http,
Expand All @@ -72,17 +69,17 @@ const AttackDiscoveryPageComponent: React.FC = () => {

// time selection:
const [start, setStart] = useLocalStorage<string>(
`${DEFAULT_ASSISTANT_NAMESPACE}.${ATTACK_DISCOVERY_STORAGE_KEY}.${spaceId}.${START_LOCAL_STORAGE_KEY}`,
`${DEFAULT_ASSISTANT_NAMESPACE}.${ATTACK_DISCOVERY_STORAGE_KEY}.${START_LOCAL_STORAGE_KEY}`,
DEFAULT_START
);
const [end, setEnd] = useLocalStorage<string>(
`${DEFAULT_ASSISTANT_NAMESPACE}.${ATTACK_DISCOVERY_STORAGE_KEY}.${spaceId}.${END_LOCAL_STORAGE_KEY}`,
`${DEFAULT_ASSISTANT_NAMESPACE}.${ATTACK_DISCOVERY_STORAGE_KEY}.${END_LOCAL_STORAGE_KEY}`,
DEFAULT_END
);

// search bar query:
const [query, setQuery] = useLocalStorage<Query>(
`${DEFAULT_ASSISTANT_NAMESPACE}.${ATTACK_DISCOVERY_STORAGE_KEY}.${spaceId}.${QUERY_LOCAL_STORAGE_KEY}`,
`${DEFAULT_ASSISTANT_NAMESPACE}.${ATTACK_DISCOVERY_STORAGE_KEY}.${QUERY_LOCAL_STORAGE_KEY}`,
getDefaultQuery(),
{
raw: false,
Expand All @@ -93,7 +90,7 @@ const AttackDiscoveryPageComponent: React.FC = () => {

// search bar filters:
const [filters, setFilters] = useLocalStorage<Filter[]>(
`${DEFAULT_ASSISTANT_NAMESPACE}.${ATTACK_DISCOVERY_STORAGE_KEY}.${spaceId}.${FILTERS_LOCAL_STORAGE_KEY}`,
`${DEFAULT_ASSISTANT_NAMESPACE}.${ATTACK_DISCOVERY_STORAGE_KEY}.${FILTERS_LOCAL_STORAGE_KEY}`,
[],
{
raw: false,
Expand All @@ -107,12 +104,12 @@ const AttackDiscoveryPageComponent: React.FC = () => {
// get the last selected connector ID from local storage:
const [localStorageAttackDiscoveryConnectorId, setLocalStorageAttackDiscoveryConnectorId] =
useLocalStorage<string>(
`${DEFAULT_ASSISTANT_NAMESPACE}.${ATTACK_DISCOVERY_STORAGE_KEY}.${spaceId}.${CONNECTOR_ID_LOCAL_STORAGE_KEY}`
`${DEFAULT_ASSISTANT_NAMESPACE}.${ATTACK_DISCOVERY_STORAGE_KEY}.${CONNECTOR_ID_LOCAL_STORAGE_KEY}`
);

const [localStorageAttackDiscoveryMaxAlerts, setLocalStorageAttackDiscoveryMaxAlerts] =
useLocalStorage<string>(
`${DEFAULT_ASSISTANT_NAMESPACE}.${ATTACK_DISCOVERY_STORAGE_KEY}.${spaceId}.${MAX_ALERTS_LOCAL_STORAGE_KEY}`,
`${DEFAULT_ASSISTANT_NAMESPACE}.${ATTACK_DISCOVERY_STORAGE_KEY}.${MAX_ALERTS_LOCAL_STORAGE_KEY}`,
`${DEFAULT_ATTACK_DISCOVERY_MAX_ALERTS}`
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,8 @@ const AnimatedCounterComponent: React.FC<Props> = ({ animationDurationMs = 1000

const text = svg
.append('text')
.attr('x', 3)
.attr('y', 26)
.attr('fill', euiTheme.colors.text)
.attr('y', 24)
.attr('fill', euiTheme.colors.textHeading)
.text(zero);

text
Expand All @@ -45,14 +44,14 @@ const AnimatedCounterComponent: React.FC<Props> = ({ animationDurationMs = 1000
})
.duration(animationDurationMs);
}
}, [animationDurationMs, count, euiTheme.colors.text]);
}, [animationDurationMs, count, euiTheme.colors.textHeading]);

return (
<svg
css={css`
height: 32px;
margin-right: ${euiTheme.size.xs};
width: ${count < 100 ? 40 : 60}px;
width: ${count < 100 ? 32 : 48}px;
`}
data-test-subj="animatedCounter"
ref={d3Ref}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
* 2.0.
*/

import React from 'react';
import type { FilterManager } from '@kbn/data-plugin/public';
import { render, screen } from '@testing-library/react';
import React from 'react';

import { AlertSelectionQuery } from '.';
import { useKibana } from '../../../../../common/lib/kibana';
Expand All @@ -24,6 +25,7 @@ const mockUseSourcererDataView = useSourcererDataView as jest.MockedFunction<
describe('AlertSelectionQuery', () => {
const defaultProps = {
end: 'now',
filterManager: jest.fn() as unknown as FilterManager,
filters: [],
query: { query: '', language: 'kuery' },
setEnd: jest.fn(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import type { OnTimeChangeProps } from '@elastic/eui';
import { EuiSuperDatePicker, EuiSpacer } from '@elastic/eui';
import { css } from '@emotion/react';
import type { FilterManager } from '@kbn/data-plugin/public';
import type { DataView } from '@kbn/data-views-plugin/common';
import type { Filter, Query } from '@kbn/es-query';
import { debounce } from 'lodash/fp';
Expand All @@ -17,7 +18,6 @@ import { useKibana } from '../../../../../common/lib/kibana';
import { getCommonTimeRanges } from '../helpers/get_common_time_ranges';
import { useSourcererDataView } from '../../../../../sourcerer/containers';
import { SourcererScopeName } from '../../../../../sourcerer/store/model';
import * as i18n from '../translations';
import { useDataView } from '../use_data_view';

export const MAX_ALERTS = 500;
Expand All @@ -27,21 +27,21 @@ export const NO_INDEX_PATTERNS: DataView[] = [];

interface Props {
end: string;
filterManager: FilterManager;
filters: Filter[];
query: Query;
setEnd: React.Dispatch<React.SetStateAction<string>>;
setFilters: React.Dispatch<React.SetStateAction<Filter[]>>;
setQuery: React.Dispatch<React.SetStateAction<Query>>;
setStart: React.Dispatch<React.SetStateAction<string>>;
start: string;
}

const AlertSelectionQueryComponent: React.FC<Props> = ({
end,
filterManager,
filters,
query,
setEnd,
setFilters,
setQuery,
setStart,
start,
Expand Down Expand Up @@ -129,9 +129,9 @@ const AlertSelectionQueryComponent: React.FC<Props> = ({
*/
const onFiltersUpdated = useCallback(
(newFilters: Filter[]) => {
setFilters(newFilters);
filterManager.setFilters(newFilters);
},
[setFilters]
[filterManager]
);

/**
Expand Down Expand Up @@ -172,7 +172,6 @@ const AlertSelectionQueryComponent: React.FC<Props> = ({
debouncedOnQueryChange(debouncedQuery?.query);
}}
onQuerySubmit={onQuerySubmit}
placeholder={i18n.FILTER_YOUR_DATA}
query={query}
/>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ describe('getAlertSummaryEsqlQuery', () => {
});

expect(query).toBe(
`FROM alerts-* METADATA _id, _index, _version
`FROM alerts-* METADATA _id, _index, _version, _ignored
| WHERE kibana.alert.workflow_status IN ("open", "acknowledged") AND kibana.alert.rule.building_block_type IS NULL
| SORT kibana.alert.risk_score DESC, @timestamp DESC
| LIMIT 100
Expand All @@ -35,7 +35,7 @@ describe('getAlertSummaryEsqlQuery', () => {
});

expect(query).toBe(
`FROM alerts-* METADATA _id, _index, _version
`FROM alerts-* METADATA _id, _index, _version, _ignored
| WHERE kibana.alert.workflow_status IN ("open", "acknowledged") AND kibana.alert.rule.building_block_type IS NULL
| SORT kibana.alert.risk_score DESC, @timestamp DESC
| LIMIT 100
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export const getAlertSummaryEsqlQuery = ({
alertsIndexPattern: string;
maxAlerts: number;
tableStackBy0: string;
}): string => `FROM ${alertsIndexPattern} METADATA _id, _index, _version
}): string => `FROM ${alertsIndexPattern} METADATA _id, _index, _version, _ignored
| WHERE kibana.alert.workflow_status IN ("open", "acknowledged") AND kibana.alert.rule.building_block_type IS NULL
| SORT kibana.alert.risk_score DESC, @timestamp DESC
| LIMIT ${maxAlerts}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ describe('getAlertsPreviewEsqlQuery', () => {
});

expect(result).toBe(
`FROM alerts-* METADATA _id, _index, _version
`FROM alerts-* METADATA _id, _index, _version, _ignored
| WHERE kibana.alert.workflow_status IN ("open", "acknowledged") AND kibana.alert.rule.building_block_type IS NULL
| SORT kibana.alert.risk_score DESC, @timestamp DESC
| LIMIT 10
Expand All @@ -33,7 +33,7 @@ describe('getAlertsPreviewEsqlQuery', () => {
});

expect(result).toBe(
`FROM alerts-* METADATA _id, _index, _version
`FROM alerts-* METADATA _id, _index, _version, _ignored
| WHERE kibana.alert.workflow_status IN ("open", "acknowledged") AND kibana.alert.rule.building_block_type IS NULL
| SORT kibana.alert.risk_score DESC, @timestamp DESC
| LIMIT 10
Expand All @@ -50,7 +50,7 @@ describe('getAlertsPreviewEsqlQuery', () => {
});

expect(result).toBe(
`FROM alerts-* METADATA _id, _index, _version
`FROM alerts-* METADATA _id, _index, _version, _ignored
| WHERE kibana.alert.workflow_status IN ("open", "acknowledged") AND kibana.alert.rule.building_block_type IS NULL
| SORT kibana.alert.risk_score DESC, @timestamp DESC
| LIMIT 10
Expand All @@ -67,7 +67,7 @@ describe('getAlertsPreviewEsqlQuery', () => {
});

expect(result).toBe(
`FROM alerts-* METADATA _id, _index, _version
`FROM alerts-* METADATA _id, _index, _version, _ignored
| WHERE kibana.alert.workflow_status IN ("open", "acknowledged") AND kibana.alert.rule.building_block_type IS NULL
| SORT kibana.alert.risk_score DESC, @timestamp DESC
| LIMIT 5
Expand All @@ -84,7 +84,7 @@ describe('getAlertsPreviewEsqlQuery', () => {
});

expect(result).toBe(
`FROM custom-alerts-* METADATA _id, _index, _version
`FROM custom-alerts-* METADATA _id, _index, _version, _ignored
| WHERE kibana.alert.workflow_status IN ("open", "acknowledged") AND kibana.alert.rule.building_block_type IS NULL
| SORT kibana.alert.risk_score DESC, @timestamp DESC
| LIMIT 10
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export const getAlertsPreviewEsqlQuery = ({
alertsIndexPattern: string;
maxAlerts: number;
tableStackBy0: string;
}): string => `FROM ${alertsIndexPattern} METADATA _id, _index, _version
}): string => `FROM ${alertsIndexPattern} METADATA _id, _index, _version, _ignored
| WHERE kibana.alert.workflow_status IN ("open", "acknowledged") AND kibana.alert.rule.building_block_type IS NULL
| SORT kibana.alert.risk_score DESC, @timestamp DESC
| LIMIT ${maxAlerts}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
* 2.0.
*/

import React from 'react';
import type { FilterManager } from '@kbn/data-plugin/public';
import { render, screen, fireEvent } from '@testing-library/react';
import React from 'react';

import { AlertSelection } from '.';
import { useKibana } from '../../../../common/lib/kibana';
Expand All @@ -28,13 +29,13 @@ const defaultProps = {
alertsPreviewStackBy0: 'defaultAlertPreview',
alertSummaryStackBy0: 'defaultAlertSummary',
end: '2024-10-01T00:00:00.000Z',
filterManager: jest.fn() as unknown as FilterManager,
filters: [],
maxAlerts: 100,
query: { query: '', language: 'kuery' },
setAlertsPreviewStackBy0: jest.fn(),
setAlertSummaryStackBy0: jest.fn(),
setEnd: jest.fn(),
setFilters: jest.fn(),
setMaxAlerts: jest.fn(),
setQuery: jest.fn(),
setStart: jest.fn(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/

import { EuiFlexGroup, EuiFlexItem, EuiTab, EuiTabs, EuiText, EuiSpacer } from '@elastic/eui';
import type { FilterManager } from '@kbn/data-plugin/public';
import type { Filter, Query } from '@kbn/es-query';
import React, { useMemo, useState } from 'react';

Expand All @@ -18,13 +19,13 @@ interface Props {
alertsPreviewStackBy0: string;
alertSummaryStackBy0: string;
end: string;
filterManager: FilterManager;
filters: Filter[];
maxAlerts: number;
query: Query;
setAlertsPreviewStackBy0: React.Dispatch<React.SetStateAction<string>>;
setAlertSummaryStackBy0: React.Dispatch<React.SetStateAction<string>>;
setEnd: React.Dispatch<React.SetStateAction<string>>;
setFilters: React.Dispatch<React.SetStateAction<Filter[]>>;
setMaxAlerts: React.Dispatch<React.SetStateAction<string>>;
setQuery: React.Dispatch<React.SetStateAction<Query>>;
setStart: React.Dispatch<React.SetStateAction<string>>;
Expand All @@ -35,13 +36,13 @@ const AlertSelectionComponent: React.FC<Props> = ({
alertsPreviewStackBy0,
alertSummaryStackBy0,
end,
filterManager,
filters,
maxAlerts,
query,
setAlertsPreviewStackBy0,
setAlertSummaryStackBy0,
setEnd,
setFilters,
setMaxAlerts,
setQuery,
setStart,
Expand Down Expand Up @@ -95,10 +96,10 @@ const AlertSelectionComponent: React.FC<Props> = ({
<EuiFlexItem grow={false}>
<AlertSelectionQuery
end={end}
filterManager={filterManager}
filters={filters}
query={query}
setEnd={setEnd}
setFilters={setFilters}
setQuery={setQuery}
setStart={setStart}
start={start}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import { TestProviders } from '../../../../../common/mock';
import { useSignalIndex } from '../../../../../detections/containers/detection_engine/alerts/use_signal_index';
import { useSourcererDataView } from '../../../../../sourcerer/containers';

const mockDispatch = jest.fn();

jest.mock('../../../../../common/lib/kibana');
jest.mock('../../../../../sourcerer/containers');
jest.mock('../../../../../detections/containers/detection_engine/alerts/use_signal_index');
Expand All @@ -24,6 +26,10 @@ jest.mock('react-router-dom', () => ({
}),
withRouter: jest.fn(),
}));
jest.mock('react-redux', () => ({
...jest.requireActual('react-redux'),
useDispatch: () => mockDispatch,
}));

const mockUseKibana = useKibana as jest.MockedFunction<typeof useKibana>;
const mockUseSourcererDataView = useSourcererDataView as jest.MockedFunction<
Expand Down Expand Up @@ -154,4 +160,22 @@ describe('PreviewTab', () => {

expect(container.firstChild).toBeNull();
});

it('limits the fields in the StackByComboBox to the fields in the signal index', () => {
render(
<TestProviders>
<PreviewTab {...defaultProps} />
</TestProviders>
);

expect(mockDispatch).toHaveBeenCalledWith({
payload: {
id: 'detections',
selectedDataViewId: 'mock-signal-index',
selectedPatterns: ['mock-signal-index'],
shouldValidateSelectedPatterns: false,
},
type: 'x-pack/security_solution/local/sourcerer/SET_SELECTED_DATA_VIEW',
});
});
});
Loading